From 33f5b697290aa0c9466822b4f11718628679ffe8 Mon Sep 17 00:00:00 2001 From: Ioannis Tziakos Date: Sat, 28 Sep 2024 14:28:06 +0100 Subject: [PATCH 01/41] Copy cpython tests from 3.12.6 --- cpython-tests/README.rst | 2 + cpython-tests/test_zipfile/__init__.py | 5 + cpython-tests/test_zipfile/__main__.py | 7 + cpython-tests/test_zipfile/_path/__init__.py | 0 .../test_zipfile/_path/_functools.py | 9 + .../test_zipfile/_path/_itertools.py | 79 + cpython-tests/test_zipfile/_path/_support.py | 9 + .../test_zipfile/_path/_test_params.py | 39 + .../test_zipfile/_path/test_complexity.py | 103 + cpython-tests/test_zipfile/_path/test_path.py | 676 ++++ .../test_zipfile/_path/write-alpharep.py | 4 + cpython-tests/test_zipfile/test_core.py | 3432 +++++++++++++++++ cpython-tests/test_zipfile64.py | 144 + cpython-tests/test_zipimport.py | 847 ++++ cpython-tests/test_zipimport_support.py | 244 ++ cpython-tests/zip_cp437_header.zip | Bin 0 -> 270 bytes cpython-tests/zipdir.zip | Bin 0 -> 374 bytes cpython-tests/zipdir_backslash.zip | Bin 0 -> 192 bytes cpython-tests/ziptestdata/README.md | 35 + cpython-tests/ziptestdata/exe_with_z64 | Bin 0 -> 978 bytes cpython-tests/ziptestdata/exe_with_zip | Bin 0 -> 990 bytes cpython-tests/ziptestdata/header.sh | 24 + .../ziptestdata/testdata_module_inside_zip.py | 2 + 23 files changed, 5661 insertions(+) create mode 100644 cpython-tests/README.rst create mode 100644 cpython-tests/test_zipfile/__init__.py create mode 100644 cpython-tests/test_zipfile/__main__.py create mode 100644 cpython-tests/test_zipfile/_path/__init__.py create mode 100644 cpython-tests/test_zipfile/_path/_functools.py create mode 100644 cpython-tests/test_zipfile/_path/_itertools.py create mode 100644 cpython-tests/test_zipfile/_path/_support.py create mode 100644 cpython-tests/test_zipfile/_path/_test_params.py create mode 100644 cpython-tests/test_zipfile/_path/test_complexity.py create mode 100644 cpython-tests/test_zipfile/_path/test_path.py create mode 100644 cpython-tests/test_zipfile/_path/write-alpharep.py create mode 100644 cpython-tests/test_zipfile/test_core.py create mode 100644 cpython-tests/test_zipfile64.py create mode 100644 cpython-tests/test_zipimport.py create mode 100644 cpython-tests/test_zipimport_support.py create mode 100644 cpython-tests/zip_cp437_header.zip create mode 100644 cpython-tests/zipdir.zip create mode 100644 cpython-tests/zipdir_backslash.zip create mode 100644 cpython-tests/ziptestdata/README.md create mode 100644 cpython-tests/ziptestdata/exe_with_z64 create mode 100644 cpython-tests/ziptestdata/exe_with_zip create mode 100644 cpython-tests/ziptestdata/header.sh create mode 100644 cpython-tests/ziptestdata/testdata_module_inside_zip.py diff --git a/cpython-tests/README.rst b/cpython-tests/README.rst new file mode 100644 index 0000000..1e42df1 --- /dev/null +++ b/cpython-tests/README.rst @@ -0,0 +1,2 @@ +Zipfile test code copied from the cpython source code. Adapted to test +against the zipfile2. diff --git a/cpython-tests/test_zipfile/__init__.py b/cpython-tests/test_zipfile/__init__.py new file mode 100644 index 0000000..4b16ecc --- /dev/null +++ b/cpython-tests/test_zipfile/__init__.py @@ -0,0 +1,5 @@ +import os +from test.support import load_package_tests + +def load_tests(*args): + return load_package_tests(os.path.dirname(__file__), *args) diff --git a/cpython-tests/test_zipfile/__main__.py b/cpython-tests/test_zipfile/__main__.py new file mode 100644 index 0000000..e25ac94 --- /dev/null +++ b/cpython-tests/test_zipfile/__main__.py @@ -0,0 +1,7 @@ +import unittest + +from . import load_tests # noqa: F401 + + +if __name__ == "__main__": + unittest.main() diff --git a/cpython-tests/test_zipfile/_path/__init__.py b/cpython-tests/test_zipfile/_path/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/cpython-tests/test_zipfile/_path/_functools.py b/cpython-tests/test_zipfile/_path/_functools.py new file mode 100644 index 0000000..75f2b20 --- /dev/null +++ b/cpython-tests/test_zipfile/_path/_functools.py @@ -0,0 +1,9 @@ +import functools + + +# from jaraco.functools 3.5.2 +def compose(*funcs): + def compose_two(f1, f2): + return lambda *args, **kwargs: f1(f2(*args, **kwargs)) + + return functools.reduce(compose_two, funcs) diff --git a/cpython-tests/test_zipfile/_path/_itertools.py b/cpython-tests/test_zipfile/_path/_itertools.py new file mode 100644 index 0000000..f735dd2 --- /dev/null +++ b/cpython-tests/test_zipfile/_path/_itertools.py @@ -0,0 +1,79 @@ +import itertools +from collections import deque +from itertools import islice + + +# from jaraco.itertools 6.3.0 +class Counter: + """ + Wrap an iterable in an object that stores the count of items + that pass through it. + + >>> items = Counter(range(20)) + >>> items.count + 0 + >>> values = list(items) + >>> items.count + 20 + """ + + def __init__(self, i): + self.count = 0 + self.iter = zip(itertools.count(1), i) + + def __iter__(self): + return self + + def __next__(self): + self.count, result = next(self.iter) + return result + + +# from more_itertools v8.13.0 +def always_iterable(obj, base_type=(str, bytes)): + if obj is None: + return iter(()) + + if (base_type is not None) and isinstance(obj, base_type): + return iter((obj,)) + + try: + return iter(obj) + except TypeError: + return iter((obj,)) + + +# from more_itertools v9.0.0 +def consume(iterator, n=None): + """Advance *iterable* by *n* steps. If *n* is ``None``, consume it + entirely. + Efficiently exhausts an iterator without returning values. Defaults to + consuming the whole iterator, but an optional second argument may be + provided to limit consumption. + >>> i = (x for x in range(10)) + >>> next(i) + 0 + >>> consume(i, 3) + >>> next(i) + 4 + >>> consume(i) + >>> next(i) + Traceback (most recent call last): + File "", line 1, in + StopIteration + If the iterator has fewer items remaining than the provided limit, the + whole iterator will be consumed. + >>> i = (x for x in range(3)) + >>> consume(i, 5) + >>> next(i) + Traceback (most recent call last): + File "", line 1, in + StopIteration + """ + # Use functions that consume iterators at C speed. + if n is None: + # feed the entire iterator into a zero-length deque + deque(iterator, maxlen=0) + else: + # advance to the empty slice starting at position n + next(islice(iterator, n, n), None) diff --git a/cpython-tests/test_zipfile/_path/_support.py b/cpython-tests/test_zipfile/_path/_support.py new file mode 100644 index 0000000..1afdf3b --- /dev/null +++ b/cpython-tests/test_zipfile/_path/_support.py @@ -0,0 +1,9 @@ +import importlib +import unittest + + +def import_or_skip(name): + try: + return importlib.import_module(name) + except ImportError: # pragma: no cover + raise unittest.SkipTest(f'Unable to import {name}') diff --git a/cpython-tests/test_zipfile/_path/_test_params.py b/cpython-tests/test_zipfile/_path/_test_params.py new file mode 100644 index 0000000..bc95b4e --- /dev/null +++ b/cpython-tests/test_zipfile/_path/_test_params.py @@ -0,0 +1,39 @@ +import types +import functools + +from ._itertools import always_iterable + + +def parameterize(names, value_groups): + """ + Decorate a test method to run it as a set of subtests. + + Modeled after pytest.parametrize. + """ + + def decorator(func): + @functools.wraps(func) + def wrapped(self): + for values in value_groups: + resolved = map(Invoked.eval, always_iterable(values)) + params = dict(zip(always_iterable(names), resolved)) + with self.subTest(**params): + func(self, **params) + + return wrapped + + return decorator + + +class Invoked(types.SimpleNamespace): + """ + Wrap a function to be invoked for each usage. + """ + + @classmethod + def wrap(cls, func): + return cls(func=func) + + @classmethod + def eval(cls, cand): + return cand.func() if isinstance(cand, cls) else cand diff --git a/cpython-tests/test_zipfile/_path/test_complexity.py b/cpython-tests/test_zipfile/_path/test_complexity.py new file mode 100644 index 0000000..2286176 --- /dev/null +++ b/cpython-tests/test_zipfile/_path/test_complexity.py @@ -0,0 +1,103 @@ +import io +import itertools +import math +import re +import string +import unittest +import zipfile + +from ._functools import compose +from ._itertools import consume + +from ._support import import_or_skip + + +big_o = import_or_skip('big_o') +pytest = import_or_skip('pytest') + + +class TestComplexity(unittest.TestCase): + @pytest.mark.flaky + def test_implied_dirs_performance(self): + best, others = big_o.big_o( + compose(consume, zipfile._path.CompleteDirs._implied_dirs), + lambda size: [ + '/'.join(string.ascii_lowercase + str(n)) for n in range(size) + ], + max_n=1000, + min_n=1, + ) + assert best <= big_o.complexities.Linear + + def make_zip_path(self, depth=1, width=1) -> zipfile.Path: + """ + Construct a Path with width files at every level of depth. + """ + zf = zipfile.ZipFile(io.BytesIO(), mode='w') + pairs = itertools.product(self.make_deep_paths(depth), self.make_names(width)) + for path, name in pairs: + zf.writestr(f"{path}{name}.txt", b'') + zf.filename = "big un.zip" + return zipfile.Path(zf) + + @classmethod + def make_names(cls, width, letters=string.ascii_lowercase): + """ + >>> list(TestComplexity.make_names(2)) + ['a', 'b'] + >>> list(TestComplexity.make_names(30)) + ['aa', 'ab', ..., 'bd'] + """ + # determine how many products are needed to produce width + n_products = math.ceil(math.log(width, len(letters))) + inputs = (letters,) * n_products + combinations = itertools.product(*inputs) + names = map(''.join, combinations) + return itertools.islice(names, width) + + @classmethod + def make_deep_paths(cls, depth): + return map(cls.make_deep_path, range(depth)) + + @classmethod + def make_deep_path(cls, depth): + return ''.join(('d/',) * depth) + + def test_baseline_regex_complexity(self): + best, others = big_o.big_o( + lambda path: re.fullmatch(r'[^/]*\\.txt', path), + self.make_deep_path, + max_n=100, + min_n=1, + ) + assert best <= big_o.complexities.Constant + + @pytest.mark.flaky + def test_glob_depth(self): + best, others = big_o.big_o( + lambda path: consume(path.glob('*.txt')), + self.make_zip_path, + max_n=100, + min_n=1, + ) + assert best <= big_o.complexities.Quadratic + + @pytest.mark.flaky + def test_glob_width(self): + best, others = big_o.big_o( + lambda path: consume(path.glob('*.txt')), + lambda size: self.make_zip_path(width=size), + max_n=100, + min_n=1, + ) + assert best <= big_o.complexities.Linear + + @pytest.mark.flaky + def test_glob_width_and_depth(self): + best, others = big_o.big_o( + lambda path: consume(path.glob('*.txt')), + lambda size: self.make_zip_path(depth=size, width=size), + max_n=10, + min_n=1, + ) + assert best <= big_o.complexities.Linear diff --git a/cpython-tests/test_zipfile/_path/test_path.py b/cpython-tests/test_zipfile/_path/test_path.py new file mode 100644 index 0000000..616c4e8 --- /dev/null +++ b/cpython-tests/test_zipfile/_path/test_path.py @@ -0,0 +1,676 @@ +import io +import itertools +import contextlib +import pathlib +import pickle +import sys +import time +import unittest +import zipfile + +from test.support.os_helper import temp_dir, FakePath + +from ._functools import compose +from ._itertools import Counter + +from ._test_params import parameterize, Invoked + + +class jaraco: + class itertools: + Counter = Counter + + +def add_dirs(zf): + """ + Given a writable zip file zf, inject directory entries for + any directories implied by the presence of children. + """ + for name in zipfile.CompleteDirs._implied_dirs(zf.namelist()): + zf.writestr(name, b"") + return zf + + +def build_alpharep_fixture(): + """ + Create a zip file with this structure: + + . + ├── a.txt + ├── b + │ ├── c.txt + │ ├── d + │ │ └── e.txt + │ └── f.txt + ├── g + │ └── h + │ └── i.txt + └── j + ├── k.bin + ├── l.baz + └── m.bar + + This fixture has the following key characteristics: + + - a file at the root (a) + - a file two levels deep (b/d/e) + - multiple files in a directory (b/c, b/f) + - a directory containing only a directory (g/h) + - a directory with files of different extensions (j/klm) + + "alpha" because it uses alphabet + "rep" because it's a representative example + """ + data = io.BytesIO() + zf = zipfile.ZipFile(data, "w") + zf.writestr("a.txt", b"content of a") + zf.writestr("b/c.txt", b"content of c") + zf.writestr("b/d/e.txt", b"content of e") + zf.writestr("b/f.txt", b"content of f") + zf.writestr("g/h/i.txt", b"content of i") + zf.writestr("j/k.bin", b"content of k") + zf.writestr("j/l.baz", b"content of l") + zf.writestr("j/m.bar", b"content of m") + zf.filename = "alpharep.zip" + return zf + + +alpharep_generators = [ + Invoked.wrap(build_alpharep_fixture), + Invoked.wrap(compose(add_dirs, build_alpharep_fixture)), +] + +pass_alpharep = parameterize(['alpharep'], alpharep_generators) + + +class TestPath(unittest.TestCase): + def setUp(self): + self.fixtures = contextlib.ExitStack() + self.addCleanup(self.fixtures.close) + + def zipfile_ondisk(self, alpharep): + tmpdir = pathlib.Path(self.fixtures.enter_context(temp_dir())) + buffer = alpharep.fp + alpharep.close() + path = tmpdir / alpharep.filename + with path.open("wb") as strm: + strm.write(buffer.getvalue()) + return path + + @pass_alpharep + def test_iterdir_and_types(self, alpharep): + root = zipfile.Path(alpharep) + assert root.is_dir() + a, b, g, j = root.iterdir() + assert a.is_file() + assert b.is_dir() + assert g.is_dir() + c, f, d = b.iterdir() + assert c.is_file() and f.is_file() + (e,) = d.iterdir() + assert e.is_file() + (h,) = g.iterdir() + (i,) = h.iterdir() + assert i.is_file() + + @pass_alpharep + def test_is_file_missing(self, alpharep): + root = zipfile.Path(alpharep) + assert not root.joinpath('missing.txt').is_file() + + @pass_alpharep + def test_iterdir_on_file(self, alpharep): + root = zipfile.Path(alpharep) + a, b, g, j = root.iterdir() + with self.assertRaises(ValueError): + a.iterdir() + + @pass_alpharep + def test_subdir_is_dir(self, alpharep): + root = zipfile.Path(alpharep) + assert (root / 'b').is_dir() + assert (root / 'b/').is_dir() + assert (root / 'g').is_dir() + assert (root / 'g/').is_dir() + + @pass_alpharep + def test_open(self, alpharep): + root = zipfile.Path(alpharep) + a, b, g, j = root.iterdir() + with a.open(encoding="utf-8") as strm: + data = strm.read() + self.assertEqual(data, "content of a") + with a.open('r', "utf-8") as strm: # not a kw, no gh-101144 TypeError + data = strm.read() + self.assertEqual(data, "content of a") + + def test_open_encoding_utf16(self): + in_memory_file = io.BytesIO() + zf = zipfile.ZipFile(in_memory_file, "w") + zf.writestr("path/16.txt", "This was utf-16".encode("utf-16")) + zf.filename = "test_open_utf16.zip" + root = zipfile.Path(zf) + (path,) = root.iterdir() + u16 = path.joinpath("16.txt") + with u16.open('r', "utf-16") as strm: + data = strm.read() + assert data == "This was utf-16" + with u16.open(encoding="utf-16") as strm: + data = strm.read() + assert data == "This was utf-16" + + def test_open_encoding_errors(self): + in_memory_file = io.BytesIO() + zf = zipfile.ZipFile(in_memory_file, "w") + zf.writestr("path/bad-utf8.bin", b"invalid utf-8: \xff\xff.") + zf.filename = "test_read_text_encoding_errors.zip" + root = zipfile.Path(zf) + (path,) = root.iterdir() + u16 = path.joinpath("bad-utf8.bin") + + # encoding= as a positional argument for gh-101144. + data = u16.read_text("utf-8", errors="ignore") + assert data == "invalid utf-8: ." + with u16.open("r", "utf-8", errors="surrogateescape") as f: + assert f.read() == "invalid utf-8: \udcff\udcff." + + # encoding= both positional and keyword is an error; gh-101144. + with self.assertRaisesRegex(TypeError, "encoding"): + data = u16.read_text("utf-8", encoding="utf-8") + + # both keyword arguments work. + with u16.open("r", encoding="utf-8", errors="strict") as f: + # error during decoding with wrong codec. + with self.assertRaises(UnicodeDecodeError): + f.read() + + @unittest.skipIf( + not getattr(sys.flags, 'warn_default_encoding', 0), + "Requires warn_default_encoding", + ) + @pass_alpharep + def test_encoding_warnings(self, alpharep): + """EncodingWarning must blame the read_text and open calls.""" + assert sys.flags.warn_default_encoding + root = zipfile.Path(alpharep) + with self.assertWarns(EncodingWarning) as wc: + root.joinpath("a.txt").read_text() + assert __file__ == wc.filename + with self.assertWarns(EncodingWarning) as wc: + root.joinpath("a.txt").open("r").close() + assert __file__ == wc.filename + + 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', encoding="utf-8") 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() + + @pass_alpharep + def test_open_binary_invalid_args(self, alpharep): + root = zipfile.Path(alpharep) + with self.assertRaises(ValueError): + root.joinpath('a.txt').open('rb', encoding='utf-8') + with self.assertRaises(ValueError): + root.joinpath('a.txt').open('rb', 'utf-8') + + 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() + + @pass_alpharep + def test_read(self, alpharep): + root = zipfile.Path(alpharep) + a, b, g, j = root.iterdir() + assert a.read_text(encoding="utf-8") == "content of a" + # Also check positional encoding arg (gh-101144). + assert a.read_text("utf-8") == "content of a" + assert a.read_bytes() == b"content of a" + + @pass_alpharep + def test_joinpath(self, alpharep): + root = zipfile.Path(alpharep) + a = root.joinpath("a.txt") + assert a.is_file() + e = root.joinpath("b").joinpath("d").joinpath("e.txt") + assert e.read_text(encoding="utf-8") == "content of e" + + @pass_alpharep + def test_joinpath_multiple(self, alpharep): + root = zipfile.Path(alpharep) + e = root.joinpath("b", "d", "e.txt") + assert e.read_text(encoding="utf-8") == "content of e" + + @pass_alpharep + def test_traverse_truediv(self, alpharep): + root = zipfile.Path(alpharep) + a = root / "a.txt" + assert a.is_file() + e = root / "b" / "d" / "e.txt" + assert e.read_text(encoding="utf-8") == "content of e" + + @pass_alpharep + def test_pathlike_construction(self, alpharep): + """ + zipfile.Path should be constructable from a path-like object + """ + zipfile_ondisk = self.zipfile_ondisk(alpharep) + pathlike = FakePath(str(zipfile_ondisk)) + zipfile.Path(pathlike) + + @pass_alpharep + def test_traverse_pathlike(self, alpharep): + root = zipfile.Path(alpharep) + root / FakePath("a") + + @pass_alpharep + def test_parent(self, alpharep): + root = zipfile.Path(alpharep) + assert (root / 'a').parent.at == '' + assert (root / 'a' / 'b').parent.at == 'a/' + + @pass_alpharep + def test_dir_parent(self, alpharep): + root = zipfile.Path(alpharep) + assert (root / 'b').parent.at == '' + assert (root / 'b/').parent.at == '' + + @pass_alpharep + def test_missing_dir_parent(self, alpharep): + root = zipfile.Path(alpharep) + assert (root / 'missing dir/').parent.at == '' + + @pass_alpharep + def test_mutability(self, alpharep): + """ + If the underlying zipfile is changed, the Path object should + reflect that change. + """ + root = zipfile.Path(alpharep) + a, b, g, j = root.iterdir() + alpharep.writestr('foo.txt', 'foo') + alpharep.writestr('bar/baz.txt', 'baz') + assert any(child.name == 'foo.txt' for child in root.iterdir()) + assert (root / 'foo.txt').read_text(encoding="utf-8") == 'foo' + (baz,) = (root / 'bar').iterdir() + assert baz.read_text(encoding="utf-8") == 'baz' + + HUGE_ZIPFILE_NUM_ENTRIES = 2**13 + + def huge_zipfile(self): + """Create a read-only zipfile with a huge number of entries entries.""" + strm = io.BytesIO() + zf = zipfile.ZipFile(strm, "w") + for entry in map(str, range(self.HUGE_ZIPFILE_NUM_ENTRIES)): + zf.writestr(entry, entry) + zf.mode = 'r' + return zf + + def test_joinpath_constant_time(self): + """ + Ensure joinpath on items in zipfile is linear time. + """ + root = zipfile.Path(self.huge_zipfile()) + entries = jaraco.itertools.Counter(root.iterdir()) + for entry in entries: + entry.joinpath('suffix') + # Check the file iterated all items + assert entries.count == self.HUGE_ZIPFILE_NUM_ENTRIES + + @pass_alpharep + def test_read_does_not_close(self, alpharep): + alpharep = self.zipfile_ondisk(alpharep) + with zipfile.ZipFile(alpharep) as file: + for rep in range(2): + zipfile.Path(file, 'a.txt').read_text(encoding="utf-8") + + @pass_alpharep + def test_subclass(self, alpharep): + class Subclass(zipfile.Path): + pass + + root = Subclass(alpharep) + assert isinstance(root / 'b', Subclass) + + @pass_alpharep + def test_filename(self, alpharep): + root = zipfile.Path(alpharep) + assert root.filename == pathlib.Path('alpharep.zip') + + @pass_alpharep + def test_root_name(self, alpharep): + """ + The name of the root should be the name of the zipfile + """ + root = zipfile.Path(alpharep) + assert root.name == 'alpharep.zip' == root.filename.name + + @pass_alpharep + def test_suffix(self, alpharep): + """ + The suffix of the root should be the suffix of the zipfile. + The suffix of each nested file is the final component's last suffix, if any. + Includes the leading period, just like pathlib.Path. + """ + root = zipfile.Path(alpharep) + assert root.suffix == '.zip' == root.filename.suffix + + b = root / "b.txt" + assert b.suffix == ".txt" + + c = root / "c" / "filename.tar.gz" + assert c.suffix == ".gz" + + d = root / "d" + assert d.suffix == "" + + @pass_alpharep + def test_suffixes(self, alpharep): + """ + The suffix of the root should be the suffix of the zipfile. + The suffix of each nested file is the final component's last suffix, if any. + Includes the leading period, just like pathlib.Path. + """ + root = zipfile.Path(alpharep) + assert root.suffixes == ['.zip'] == root.filename.suffixes + + b = root / 'b.txt' + assert b.suffixes == ['.txt'] + + c = root / 'c' / 'filename.tar.gz' + assert c.suffixes == ['.tar', '.gz'] + + d = root / 'd' + assert d.suffixes == [] + + e = root / '.hgrc' + assert e.suffixes == [] + + @pass_alpharep + def test_suffix_no_filename(self, alpharep): + alpharep.filename = None + root = zipfile.Path(alpharep) + assert root.joinpath('example').suffix == "" + assert root.joinpath('example').suffixes == [] + + @pass_alpharep + def test_stem(self, alpharep): + """ + The final path component, without its suffix + """ + root = zipfile.Path(alpharep) + assert root.stem == 'alpharep' == root.filename.stem + + b = root / "b.txt" + assert b.stem == "b" + + c = root / "c" / "filename.tar.gz" + assert c.stem == "filename.tar" + + d = root / "d" + assert d.stem == "d" + + assert (root / ".gitignore").stem == ".gitignore" + + @pass_alpharep + def test_root_parent(self, alpharep): + root = zipfile.Path(alpharep) + assert root.parent == pathlib.Path('.') + root.root.filename = 'foo/bar.zip' + assert root.parent == pathlib.Path('foo') + + @pass_alpharep + def test_root_unnamed(self, alpharep): + """ + It is an error to attempt to get the name + or parent of an unnamed zipfile. + """ + alpharep.filename = None + root = zipfile.Path(alpharep) + with self.assertRaises(TypeError): + root.name + with self.assertRaises(TypeError): + root.parent + + # .name and .parent should still work on subs + sub = root / "b" + assert sub.name == "b" + assert sub.parent + + @pass_alpharep + def test_match_and_glob(self, alpharep): + root = zipfile.Path(alpharep) + assert not root.match("*.txt") + + assert list(root.glob("b/c.*")) == [zipfile.Path(alpharep, "b/c.txt")] + assert list(root.glob("b/*.txt")) == [ + zipfile.Path(alpharep, "b/c.txt"), + zipfile.Path(alpharep, "b/f.txt"), + ] + + @pass_alpharep + def test_glob_recursive(self, alpharep): + root = zipfile.Path(alpharep) + files = root.glob("**/*.txt") + assert all(each.match("*.txt") for each in files) + + assert list(root.glob("**/*.txt")) == list(root.rglob("*.txt")) + + @pass_alpharep + def test_glob_dirs(self, alpharep): + root = zipfile.Path(alpharep) + assert list(root.glob('b')) == [zipfile.Path(alpharep, "b/")] + assert list(root.glob('b*')) == [zipfile.Path(alpharep, "b/")] + + @pass_alpharep + def test_glob_subdir(self, alpharep): + root = zipfile.Path(alpharep) + assert list(root.glob('g/h')) == [zipfile.Path(alpharep, "g/h/")] + assert list(root.glob('g*/h*')) == [zipfile.Path(alpharep, "g/h/")] + + @pass_alpharep + def test_glob_subdirs(self, alpharep): + root = zipfile.Path(alpharep) + + assert list(root.glob("*/i.txt")) == [] + assert list(root.rglob("*/i.txt")) == [zipfile.Path(alpharep, "g/h/i.txt")] + + @pass_alpharep + def test_glob_does_not_overmatch_dot(self, alpharep): + root = zipfile.Path(alpharep) + + assert list(root.glob("*.xt")) == [] + + @pass_alpharep + def test_glob_single_char(self, alpharep): + root = zipfile.Path(alpharep) + + assert list(root.glob("a?txt")) == [zipfile.Path(alpharep, "a.txt")] + assert list(root.glob("a[.]txt")) == [zipfile.Path(alpharep, "a.txt")] + assert list(root.glob("a[?]txt")) == [] + + @pass_alpharep + def test_glob_chars(self, alpharep): + root = zipfile.Path(alpharep) + + assert list(root.glob("j/?.b[ai][nz]")) == [ + zipfile.Path(alpharep, "j/k.bin"), + zipfile.Path(alpharep, "j/l.baz"), + ] + + def test_glob_empty(self): + root = zipfile.Path(zipfile.ZipFile(io.BytesIO(), 'w')) + with self.assertRaises(ValueError): + root.glob('') + + @pass_alpharep + def test_eq_hash(self, alpharep): + root = zipfile.Path(alpharep) + assert root == zipfile.Path(alpharep) + + assert root != (root / "a.txt") + assert (root / "a.txt") == (root / "a.txt") + + root = zipfile.Path(alpharep) + assert root in {root} + + @pass_alpharep + def test_is_symlink(self, alpharep): + """ + See python/cpython#82102 for symlink support beyond this object. + """ + + root = zipfile.Path(alpharep) + assert not root.is_symlink() + + @pass_alpharep + def test_relative_to(self, alpharep): + root = zipfile.Path(alpharep) + relative = root.joinpath("b", "c.txt").relative_to(root / "b") + assert str(relative) == "c.txt" + + relative = root.joinpath("b", "d", "e.txt").relative_to(root / "b") + assert str(relative) == "d/e.txt" + + @pass_alpharep + def test_inheritance(self, alpharep): + cls = type('PathChild', (zipfile.Path,), {}) + file = cls(alpharep).joinpath('some dir').parent + assert isinstance(file, cls) + + @parameterize( + ['alpharep', 'path_type', 'subpath'], + itertools.product( + alpharep_generators, + [str, FakePath], + ['', 'b/'], + ), + ) + def test_pickle(self, alpharep, path_type, subpath): + zipfile_ondisk = path_type(str(self.zipfile_ondisk(alpharep))) + + saved_1 = pickle.dumps(zipfile.Path(zipfile_ondisk, at=subpath)) + restored_1 = pickle.loads(saved_1) + first, *rest = restored_1.iterdir() + assert first.read_text(encoding='utf-8').startswith('content of ') + + @pass_alpharep + def test_extract_orig_with_implied_dirs(self, alpharep): + """ + A zip file wrapped in a Path should extract even with implied dirs. + """ + source_path = self.zipfile_ondisk(alpharep) + zf = zipfile.ZipFile(source_path) + # wrap the zipfile for its side effect + zipfile.Path(zf) + zf.extractall(source_path.parent) + + @pass_alpharep + def test_getinfo_missing(self, alpharep): + """ + Validate behavior of getinfo on original zipfile after wrapping. + """ + zipfile.Path(alpharep) + with self.assertRaises(KeyError): + alpharep.getinfo('does-not-exist') + + def test_malformed_paths(self): + """ + Path should handle malformed paths gracefully. + + Paths with leading slashes are not visible. + + Paths with dots are treated like regular files. + """ + data = io.BytesIO() + zf = zipfile.ZipFile(data, "w") + zf.writestr("/one-slash.txt", b"content") + zf.writestr("//two-slash.txt", b"content") + zf.writestr("../parent.txt", b"content") + zf.filename = '' + root = zipfile.Path(zf) + assert list(map(str, root.iterdir())) == ['../'] + assert root.joinpath('..').joinpath('parent.txt').read_bytes() == b'content' + + def test_unsupported_names(self): + """ + Path segments with special characters are readable. + + On some platforms or file systems, characters like + ``:`` and ``?`` are not allowed, but they are valid + in the zip file. + """ + data = io.BytesIO() + zf = zipfile.ZipFile(data, "w") + zf.writestr("path?", b"content") + zf.writestr("V: NMS.flac", b"fLaC...") + zf.filename = '' + root = zipfile.Path(zf) + contents = root.iterdir() + assert next(contents).name == 'path?' + assert next(contents).name == 'V: NMS.flac' + assert root.joinpath('V: NMS.flac').read_bytes() == b"fLaC..." + + def test_backslash_not_separator(self): + """ + In a zip file, backslashes are not separators. + """ + data = io.BytesIO() + zf = zipfile.ZipFile(data, "w") + zf.writestr(DirtyZipInfo.for_name("foo\\bar", zf), b"content") + zf.filename = '' + root = zipfile.Path(zf) + (first,) = root.iterdir() + assert not first.is_dir() + assert first.name == 'foo\\bar' + + @pass_alpharep + def test_interface(self, alpharep): + from importlib.resources.abc import Traversable + + zf = zipfile.Path(alpharep) + assert isinstance(zf, Traversable) + + +class DirtyZipInfo(zipfile.ZipInfo): + """ + Bypass name sanitization. + """ + + def __init__(self, filename, *args, **kwargs): + super().__init__(filename, *args, **kwargs) + self.filename = filename + + @classmethod + def for_name(cls, name, archive): + """ + Construct the same way that ZipFile.writestr does. + + TODO: extract this functionality and re-use + """ + self = cls(filename=name, date_time=time.localtime(time.time())[:6]) + self.compress_type = archive.compression + self.compress_level = archive.compresslevel + if self.filename.endswith('/'): # pragma: no cover + self.external_attr = 0o40775 << 16 # drwxrwxr-x + self.external_attr |= 0x10 # MS-DOS directory flag + else: + self.external_attr = 0o600 << 16 # ?rw------- + return self diff --git a/cpython-tests/test_zipfile/_path/write-alpharep.py b/cpython-tests/test_zipfile/_path/write-alpharep.py new file mode 100644 index 0000000..48c09b5 --- /dev/null +++ b/cpython-tests/test_zipfile/_path/write-alpharep.py @@ -0,0 +1,4 @@ +from . import test_path + + +__name__ == '__main__' and test_path.build_alpharep_fixture().extractall('alpharep') diff --git a/cpython-tests/test_zipfile/test_core.py b/cpython-tests/test_zipfile/test_core.py new file mode 100644 index 0000000..5b32f80 --- /dev/null +++ b/cpython-tests/test_zipfile/test_core.py @@ -0,0 +1,3432 @@ +import array +import contextlib +import importlib.util +import io +import itertools +import os +import posixpath +import struct +import subprocess +import sys +import time +import unittest +import unittest.mock as mock +import zipfile + + +from tempfile import TemporaryFile +from random import randint, random, randbytes + +from test import archiver_tests +from test.support import script_helper +from test.support import ( + findfile, requires_zlib, requires_bz2, requires_lzma, + captured_stdout, captured_stderr, requires_subprocess +) +from test.support.os_helper import ( + TESTFN, unlink, rmtree, temp_dir, temp_cwd, fd_count, FakePath +) + + +TESTFN2 = TESTFN + "2" +TESTFNDIR = TESTFN + "d" +FIXEDTEST_SIZE = 1000 +DATAFILES_DIR = 'zipfile_datafiles' + +SMALL_TEST_DATA = [('_ziptest1', '1q2w3e4r5t'), + ('ziptest2dir/_ziptest2', 'qawsedrftg'), + ('ziptest2dir/ziptest3dir/_ziptest3', 'azsxdcfvgb'), + ('ziptest2dir/ziptest3dir/ziptest4dir/_ziptest3', '6y7u8i9o0p')] + +def get_files(test): + yield TESTFN2 + with TemporaryFile() as f: + yield f + test.assertFalse(f.closed) + with io.BytesIO() as f: + yield f + test.assertFalse(f.closed) + +class AbstractTestsWithSourceFile: + @classmethod + def setUpClass(cls): + cls.line_gen = [bytes("Zipfile test line %d. random float: %f\n" % + (i, random()), "ascii") + for i in range(FIXEDTEST_SIZE)] + cls.data = b''.join(cls.line_gen) + + def setUp(self): + # Make a source file with some lines + with open(TESTFN, "wb") as fp: + fp.write(self.data) + + def make_test_archive(self, f, compression, compresslevel=None): + kwargs = {'compression': compression, 'compresslevel': compresslevel} + # Create the ZIP archive + with zipfile.ZipFile(f, "w", **kwargs) as zipfp: + zipfp.write(TESTFN, "another.name") + zipfp.write(TESTFN, TESTFN) + zipfp.writestr("strfile", self.data) + with zipfp.open('written-open-w', mode='w') as f: + for line in self.line_gen: + f.write(line) + + def zip_test(self, f, compression, compresslevel=None): + self.make_test_archive(f, compression, compresslevel) + + # Read the ZIP archive + with zipfile.ZipFile(f, "r", compression) as zipfp: + self.assertEqual(zipfp.read(TESTFN), self.data) + self.assertEqual(zipfp.read("another.name"), self.data) + self.assertEqual(zipfp.read("strfile"), self.data) + + # Print the ZIP directory + fp = io.StringIO() + zipfp.printdir(file=fp) + directory = fp.getvalue() + lines = directory.splitlines() + self.assertEqual(len(lines), 5) # Number of files + header + + self.assertIn('File Name', lines[0]) + self.assertIn('Modified', lines[0]) + self.assertIn('Size', lines[0]) + + fn, date, time_, size = lines[1].split() + self.assertEqual(fn, 'another.name') + self.assertTrue(time.strptime(date, '%Y-%m-%d')) + self.assertTrue(time.strptime(time_, '%H:%M:%S')) + self.assertEqual(size, str(len(self.data))) + + # Check the namelist + names = zipfp.namelist() + self.assertEqual(len(names), 4) + self.assertIn(TESTFN, names) + self.assertIn("another.name", names) + self.assertIn("strfile", names) + self.assertIn("written-open-w", names) + + # Check infolist + infos = zipfp.infolist() + names = [i.filename for i in infos] + self.assertEqual(len(names), 4) + self.assertIn(TESTFN, names) + self.assertIn("another.name", names) + self.assertIn("strfile", names) + self.assertIn("written-open-w", names) + for i in infos: + self.assertEqual(i.file_size, len(self.data)) + + # check getinfo + for nm in (TESTFN, "another.name", "strfile", "written-open-w"): + info = zipfp.getinfo(nm) + self.assertEqual(info.filename, nm) + self.assertEqual(info.file_size, len(self.data)) + + # Check that testzip thinks the archive is ok + # (it returns None if all contents could be read properly) + self.assertIsNone(zipfp.testzip()) + + def test_basic(self): + for f in get_files(self): + self.zip_test(f, self.compression) + + def zip_open_test(self, f, compression): + self.make_test_archive(f, compression) + + # Read the ZIP archive + with zipfile.ZipFile(f, "r", compression) as zipfp: + zipdata1 = [] + with zipfp.open(TESTFN) as zipopen1: + while True: + read_data = zipopen1.read(256) + if not read_data: + break + zipdata1.append(read_data) + + zipdata2 = [] + with zipfp.open("another.name") as zipopen2: + while True: + read_data = zipopen2.read(256) + if not read_data: + break + zipdata2.append(read_data) + + self.assertEqual(b''.join(zipdata1), self.data) + self.assertEqual(b''.join(zipdata2), self.data) + + def test_open(self): + for f in get_files(self): + self.zip_open_test(f, self.compression) + + def test_open_with_pathlike(self): + path = FakePath(TESTFN2) + self.zip_open_test(path, self.compression) + with zipfile.ZipFile(path, "r", self.compression) as zipfp: + self.assertIsInstance(zipfp.filename, str) + + def zip_random_open_test(self, f, compression): + self.make_test_archive(f, compression) + + # Read the ZIP archive + with zipfile.ZipFile(f, "r", compression) as zipfp: + zipdata1 = [] + with zipfp.open(TESTFN) as zipopen1: + while True: + read_data = zipopen1.read(randint(1, 1024)) + if not read_data: + break + zipdata1.append(read_data) + + self.assertEqual(b''.join(zipdata1), self.data) + + def test_random_open(self): + for f in get_files(self): + self.zip_random_open_test(f, self.compression) + + def zip_read1_test(self, f, compression): + self.make_test_archive(f, compression) + + # Read the ZIP archive + with zipfile.ZipFile(f, "r") as zipfp, \ + zipfp.open(TESTFN) as zipopen: + zipdata = [] + while True: + read_data = zipopen.read1(-1) + if not read_data: + break + zipdata.append(read_data) + + self.assertEqual(b''.join(zipdata), self.data) + + def test_read1(self): + for f in get_files(self): + self.zip_read1_test(f, self.compression) + + def zip_read1_10_test(self, f, compression): + self.make_test_archive(f, compression) + + # Read the ZIP archive + with zipfile.ZipFile(f, "r") as zipfp, \ + zipfp.open(TESTFN) as zipopen: + zipdata = [] + while True: + read_data = zipopen.read1(10) + self.assertLessEqual(len(read_data), 10) + if not read_data: + break + zipdata.append(read_data) + + self.assertEqual(b''.join(zipdata), self.data) + + def test_read1_10(self): + for f in get_files(self): + self.zip_read1_10_test(f, self.compression) + + def zip_readline_read_test(self, f, compression): + self.make_test_archive(f, compression) + + # Read the ZIP archive + with zipfile.ZipFile(f, "r") as zipfp, \ + zipfp.open(TESTFN) as zipopen: + data = b'' + while True: + read = zipopen.readline() + if not read: + break + data += read + + read = zipopen.read(100) + if not read: + break + data += read + + self.assertEqual(data, self.data) + + def test_readline_read(self): + # Issue #7610: calls to readline() interleaved with calls to read(). + for f in get_files(self): + self.zip_readline_read_test(f, self.compression) + + def zip_readline_test(self, f, compression): + self.make_test_archive(f, compression) + + # Read the ZIP archive + with zipfile.ZipFile(f, "r") as zipfp: + with zipfp.open(TESTFN) as zipopen: + for line in self.line_gen: + linedata = zipopen.readline() + self.assertEqual(linedata, line) + + def test_readline(self): + for f in get_files(self): + self.zip_readline_test(f, self.compression) + + def zip_readlines_test(self, f, compression): + self.make_test_archive(f, compression) + + # Read the ZIP archive + with zipfile.ZipFile(f, "r") as zipfp: + with zipfp.open(TESTFN) as zipopen: + ziplines = zipopen.readlines() + for line, zipline in zip(self.line_gen, ziplines): + self.assertEqual(zipline, line) + + def test_readlines(self): + for f in get_files(self): + self.zip_readlines_test(f, self.compression) + + def zip_iterlines_test(self, f, compression): + self.make_test_archive(f, compression) + + # Read the ZIP archive + with zipfile.ZipFile(f, "r") as zipfp: + with zipfp.open(TESTFN) as zipopen: + for line, zipline in zip(self.line_gen, zipopen): + self.assertEqual(zipline, line) + + def test_iterlines(self): + for f in get_files(self): + self.zip_iterlines_test(f, self.compression) + + def test_low_compression(self): + """Check for cases where compressed data is larger than original.""" + # Create the ZIP archive + with zipfile.ZipFile(TESTFN2, "w", self.compression) as zipfp: + zipfp.writestr("strfile", '12') + + # Get an open object for strfile + with zipfile.ZipFile(TESTFN2, "r", self.compression) as zipfp: + with zipfp.open("strfile") as openobj: + self.assertEqual(openobj.read(1), b'1') + self.assertEqual(openobj.read(1), b'2') + + def test_writestr_compression(self): + zipfp = zipfile.ZipFile(TESTFN2, "w") + zipfp.writestr("b.txt", "hello world", compress_type=self.compression) + info = zipfp.getinfo('b.txt') + self.assertEqual(info.compress_type, self.compression) + + def test_writestr_compresslevel(self): + zipfp = zipfile.ZipFile(TESTFN2, "w", compresslevel=1) + zipfp.writestr("a.txt", "hello world", compress_type=self.compression) + zipfp.writestr("b.txt", "hello world", compress_type=self.compression, + compresslevel=2) + + # Compression level follows the constructor. + a_info = zipfp.getinfo('a.txt') + self.assertEqual(a_info.compress_type, self.compression) + self.assertEqual(a_info._compresslevel, 1) + + # Compression level is overridden. + b_info = zipfp.getinfo('b.txt') + self.assertEqual(b_info.compress_type, self.compression) + self.assertEqual(b_info._compresslevel, 2) + + def test_read_return_size(self): + # Issue #9837: ZipExtFile.read() shouldn't return more bytes + # than requested. + for test_size in (1, 4095, 4096, 4097, 16384): + file_size = test_size + 1 + junk = randbytes(file_size) + with zipfile.ZipFile(io.BytesIO(), "w", self.compression) as zipf: + zipf.writestr('foo', junk) + with zipf.open('foo', 'r') as fp: + buf = fp.read(test_size) + self.assertEqual(len(buf), test_size) + + def test_truncated_zipfile(self): + fp = io.BytesIO() + with zipfile.ZipFile(fp, mode='w') as zipf: + zipf.writestr('strfile', self.data, compress_type=self.compression) + end_offset = fp.tell() + zipfiledata = fp.getvalue() + + fp = io.BytesIO(zipfiledata) + with zipfile.ZipFile(fp) as zipf: + with zipf.open('strfile') as zipopen: + fp.truncate(end_offset - 20) + with self.assertRaises(EOFError): + zipopen.read() + + fp = io.BytesIO(zipfiledata) + with zipfile.ZipFile(fp) as zipf: + with zipf.open('strfile') as zipopen: + fp.truncate(end_offset - 20) + with self.assertRaises(EOFError): + while zipopen.read(100): + pass + + fp = io.BytesIO(zipfiledata) + with zipfile.ZipFile(fp) as zipf: + with zipf.open('strfile') as zipopen: + fp.truncate(end_offset - 20) + with self.assertRaises(EOFError): + while zipopen.read1(100): + pass + + def test_repr(self): + fname = 'file.name' + for f in get_files(self): + with zipfile.ZipFile(f, 'w', self.compression) as zipfp: + zipfp.write(TESTFN, fname) + r = repr(zipfp) + self.assertIn("mode='w'", r) + + with zipfile.ZipFile(f, 'r') as zipfp: + r = repr(zipfp) + if isinstance(f, str): + self.assertIn('filename=%r' % f, r) + else: + self.assertIn('file=%r' % f, r) + self.assertIn("mode='r'", r) + r = repr(zipfp.getinfo(fname)) + self.assertIn('filename=%r' % fname, r) + self.assertIn('filemode=', r) + self.assertIn('file_size=', r) + if self.compression != zipfile.ZIP_STORED: + self.assertIn('compress_type=', r) + self.assertIn('compress_size=', r) + with zipfp.open(fname) as zipopen: + r = repr(zipopen) + self.assertIn('name=%r' % fname, r) + self.assertIn("mode='r'", r) + if self.compression != zipfile.ZIP_STORED: + self.assertIn('compress_type=', r) + self.assertIn('[closed]', repr(zipopen)) + self.assertIn('[closed]', repr(zipfp)) + + def test_compresslevel_basic(self): + for f in get_files(self): + self.zip_test(f, self.compression, compresslevel=9) + + def test_per_file_compresslevel(self): + """Check that files within a Zip archive can have different + compression levels.""" + with zipfile.ZipFile(TESTFN2, "w", compresslevel=1) as zipfp: + zipfp.write(TESTFN, 'compress_1') + zipfp.write(TESTFN, 'compress_9', compresslevel=9) + one_info = zipfp.getinfo('compress_1') + nine_info = zipfp.getinfo('compress_9') + self.assertEqual(one_info._compresslevel, 1) + self.assertEqual(nine_info._compresslevel, 9) + + def test_writing_errors(self): + class BrokenFile(io.BytesIO): + def write(self, data): + nonlocal count + if count is not None: + if count == stop: + raise OSError + count += 1 + super().write(data) + + stop = 0 + while True: + testfile = BrokenFile() + count = None + with zipfile.ZipFile(testfile, 'w', self.compression) as zipfp: + with zipfp.open('file1', 'w') as f: + f.write(b'data1') + count = 0 + try: + with zipfp.open('file2', 'w') as f: + f.write(b'data2') + except OSError: + stop += 1 + else: + break + finally: + count = None + with zipfile.ZipFile(io.BytesIO(testfile.getvalue())) as zipfp: + self.assertEqual(zipfp.namelist(), ['file1']) + self.assertEqual(zipfp.read('file1'), b'data1') + + with zipfile.ZipFile(io.BytesIO(testfile.getvalue())) as zipfp: + self.assertEqual(zipfp.namelist(), ['file1', 'file2']) + self.assertEqual(zipfp.read('file1'), b'data1') + self.assertEqual(zipfp.read('file2'), b'data2') + + def test_zipextfile_attrs(self): + fname = "somefile.txt" + with zipfile.ZipFile(TESTFN2, mode="w") as zipfp: + zipfp.writestr(fname, "bogus") + + with zipfile.ZipFile(TESTFN2, mode="r") as zipfp: + with zipfp.open(fname) as fid: + self.assertEqual(fid.name, fname) + self.assertRaises(io.UnsupportedOperation, fid.fileno) + self.assertEqual(fid.mode, 'r') + self.assertIs(fid.readable(), True) + self.assertIs(fid.writable(), False) + self.assertIs(fid.seekable(), True) + self.assertIs(fid.closed, False) + self.assertIs(fid.closed, True) + self.assertEqual(fid.name, fname) + self.assertEqual(fid.mode, 'r') + self.assertRaises(io.UnsupportedOperation, fid.fileno) + self.assertRaises(ValueError, fid.readable) + self.assertIs(fid.writable(), False) + self.assertRaises(ValueError, fid.seekable) + + def tearDown(self): + unlink(TESTFN) + unlink(TESTFN2) + + +class StoredTestsWithSourceFile(AbstractTestsWithSourceFile, + unittest.TestCase): + compression = zipfile.ZIP_STORED + test_low_compression = None + + def zip_test_writestr_permissions(self, f, compression): + # Make sure that writestr and open(... mode='w') create files with + # mode 0600, when they are passed a name rather than a ZipInfo + # instance. + + self.make_test_archive(f, compression) + with zipfile.ZipFile(f, "r") as zipfp: + zinfo = zipfp.getinfo('strfile') + self.assertEqual(zinfo.external_attr, 0o600 << 16) + + zinfo2 = zipfp.getinfo('written-open-w') + self.assertEqual(zinfo2.external_attr, 0o600 << 16) + + def test_writestr_permissions(self): + for f in get_files(self): + self.zip_test_writestr_permissions(f, zipfile.ZIP_STORED) + + def test_absolute_arcnames(self): + with zipfile.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED) as zipfp: + zipfp.write(TESTFN, "/absolute") + + with zipfile.ZipFile(TESTFN2, "r", zipfile.ZIP_STORED) as zipfp: + self.assertEqual(zipfp.namelist(), ["absolute"]) + + def test_append_to_zip_file(self): + """Test appending to an existing zipfile.""" + with zipfile.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED) as zipfp: + zipfp.write(TESTFN, TESTFN) + + with zipfile.ZipFile(TESTFN2, "a", zipfile.ZIP_STORED) as zipfp: + zipfp.writestr("strfile", self.data) + self.assertEqual(zipfp.namelist(), [TESTFN, "strfile"]) + + def test_append_to_non_zip_file(self): + """Test appending to an existing file that is not a zipfile.""" + # NOTE: this test fails if len(d) < 22 because of the first + # line "fpin.seek(-22, 2)" in _EndRecData + data = b'I am not a ZipFile!'*10 + with open(TESTFN2, 'wb') as f: + f.write(data) + + with zipfile.ZipFile(TESTFN2, "a", zipfile.ZIP_STORED) as zipfp: + zipfp.write(TESTFN, TESTFN) + + with open(TESTFN2, 'rb') as f: + f.seek(len(data)) + with zipfile.ZipFile(f, "r") as zipfp: + self.assertEqual(zipfp.namelist(), [TESTFN]) + self.assertEqual(zipfp.read(TESTFN), self.data) + with open(TESTFN2, 'rb') as f: + self.assertEqual(f.read(len(data)), data) + zipfiledata = f.read() + with io.BytesIO(zipfiledata) as bio, zipfile.ZipFile(bio) as zipfp: + self.assertEqual(zipfp.namelist(), [TESTFN]) + self.assertEqual(zipfp.read(TESTFN), self.data) + + def test_read_concatenated_zip_file(self): + with io.BytesIO() as bio: + with zipfile.ZipFile(bio, 'w', zipfile.ZIP_STORED) as zipfp: + zipfp.write(TESTFN, TESTFN) + zipfiledata = bio.getvalue() + data = b'I am not a ZipFile!'*10 + with open(TESTFN2, 'wb') as f: + f.write(data) + f.write(zipfiledata) + + with zipfile.ZipFile(TESTFN2) as zipfp: + self.assertEqual(zipfp.namelist(), [TESTFN]) + self.assertEqual(zipfp.read(TESTFN), self.data) + + def test_append_to_concatenated_zip_file(self): + with io.BytesIO() as bio: + with zipfile.ZipFile(bio, 'w', zipfile.ZIP_STORED) as zipfp: + zipfp.write(TESTFN, TESTFN) + zipfiledata = bio.getvalue() + data = b'I am not a ZipFile!'*1000000 + with open(TESTFN2, 'wb') as f: + f.write(data) + f.write(zipfiledata) + + with zipfile.ZipFile(TESTFN2, 'a') as zipfp: + self.assertEqual(zipfp.namelist(), [TESTFN]) + zipfp.writestr('strfile', self.data) + + with open(TESTFN2, 'rb') as f: + self.assertEqual(f.read(len(data)), data) + zipfiledata = f.read() + with io.BytesIO(zipfiledata) as bio, zipfile.ZipFile(bio) as zipfp: + self.assertEqual(zipfp.namelist(), [TESTFN, 'strfile']) + self.assertEqual(zipfp.read(TESTFN), self.data) + self.assertEqual(zipfp.read('strfile'), self.data) + + def test_ignores_newline_at_end(self): + with zipfile.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED) as zipfp: + zipfp.write(TESTFN, TESTFN) + with open(TESTFN2, 'a', encoding='utf-8') as f: + f.write("\r\n\00\00\00") + with zipfile.ZipFile(TESTFN2, "r") as zipfp: + self.assertIsInstance(zipfp, zipfile.ZipFile) + + def test_ignores_stuff_appended_past_comments(self): + with zipfile.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED) as zipfp: + zipfp.comment = b"this is a comment" + zipfp.write(TESTFN, TESTFN) + with open(TESTFN2, 'a', encoding='utf-8') as f: + f.write("abcdef\r\n") + with zipfile.ZipFile(TESTFN2, "r") as zipfp: + self.assertIsInstance(zipfp, zipfile.ZipFile) + self.assertEqual(zipfp.comment, b"this is a comment") + + def test_write_default_name(self): + """Check that calling ZipFile.write without arcname specified + produces the expected result.""" + with zipfile.ZipFile(TESTFN2, "w") as zipfp: + zipfp.write(TESTFN) + with open(TESTFN, "rb") as f: + self.assertEqual(zipfp.read(TESTFN), f.read()) + + def test_io_on_closed_zipextfile(self): + fname = "somefile.txt" + with zipfile.ZipFile(TESTFN2, mode="w", compression=self.compression) as zipfp: + zipfp.writestr(fname, "bogus") + + with zipfile.ZipFile(TESTFN2, mode="r") as zipfp: + with zipfp.open(fname) as fid: + fid.close() + self.assertIs(fid.closed, True) + self.assertRaises(ValueError, fid.read) + self.assertRaises(ValueError, fid.seek, 0) + self.assertRaises(ValueError, fid.tell) + + def test_write_to_readonly(self): + """Check that trying to call write() on a readonly ZipFile object + raises a ValueError.""" + with zipfile.ZipFile(TESTFN2, mode="w") as zipfp: + zipfp.writestr("somefile.txt", "bogus") + + with zipfile.ZipFile(TESTFN2, mode="r") as zipfp: + self.assertRaises(ValueError, zipfp.write, TESTFN) + + with zipfile.ZipFile(TESTFN2, mode="r") as zipfp: + with self.assertRaises(ValueError): + zipfp.open(TESTFN, mode='w') + + def test_add_file_before_1980(self): + # Set atime and mtime to 1970-01-01 + os.utime(TESTFN, (0, 0)) + with zipfile.ZipFile(TESTFN2, "w") as zipfp: + self.assertRaises(ValueError, zipfp.write, TESTFN) + + with zipfile.ZipFile(TESTFN2, "w", strict_timestamps=False) as zipfp: + zipfp.write(TESTFN) + zinfo = zipfp.getinfo(TESTFN) + self.assertEqual(zinfo.date_time, (1980, 1, 1, 0, 0, 0)) + + def test_add_file_after_2107(self): + # Set atime and mtime to 2108-12-30 + ts = 4386268800 + try: + time.localtime(ts) + except OverflowError: + self.skipTest(f'time.localtime({ts}) raises OverflowError') + try: + os.utime(TESTFN, (ts, ts)) + except OverflowError: + self.skipTest('Host fs cannot set timestamp to required value.') + + mtime_ns = os.stat(TESTFN).st_mtime_ns + if mtime_ns != (4386268800 * 10**9): + # XFS filesystem is limited to 32-bit timestamp, but the syscall + # didn't fail. Moreover, there is a VFS bug which returns + # a cached timestamp which is different than the value on disk. + # + # Test st_mtime_ns rather than st_mtime to avoid rounding issues. + # + # https://bugzilla.redhat.com/show_bug.cgi?id=1795576 + # https://bugs.python.org/issue39460#msg360952 + self.skipTest(f"Linux VFS/XFS kernel bug detected: {mtime_ns=}") + + with zipfile.ZipFile(TESTFN2, "w") as zipfp: + self.assertRaises(struct.error, zipfp.write, TESTFN) + + with zipfile.ZipFile(TESTFN2, "w", strict_timestamps=False) as zipfp: + zipfp.write(TESTFN) + zinfo = zipfp.getinfo(TESTFN) + self.assertEqual(zinfo.date_time, (2107, 12, 31, 23, 59, 59)) + + +@requires_zlib() +class DeflateTestsWithSourceFile(AbstractTestsWithSourceFile, + unittest.TestCase): + compression = zipfile.ZIP_DEFLATED + + def test_per_file_compression(self): + """Check that files within a Zip archive can have different + compression options.""" + with zipfile.ZipFile(TESTFN2, "w") as zipfp: + zipfp.write(TESTFN, 'storeme', zipfile.ZIP_STORED) + zipfp.write(TESTFN, 'deflateme', zipfile.ZIP_DEFLATED) + sinfo = zipfp.getinfo('storeme') + dinfo = zipfp.getinfo('deflateme') + self.assertEqual(sinfo.compress_type, zipfile.ZIP_STORED) + self.assertEqual(dinfo.compress_type, zipfile.ZIP_DEFLATED) + +@requires_bz2() +class Bzip2TestsWithSourceFile(AbstractTestsWithSourceFile, + unittest.TestCase): + compression = zipfile.ZIP_BZIP2 + +@requires_lzma() +class LzmaTestsWithSourceFile(AbstractTestsWithSourceFile, + unittest.TestCase): + compression = zipfile.ZIP_LZMA + + +class AbstractTestZip64InSmallFiles: + # These tests test the ZIP64 functionality without using large files, + # see test_zipfile64 for proper tests. + + @classmethod + def setUpClass(cls): + line_gen = (bytes("Test of zipfile line %d." % i, "ascii") + for i in range(0, FIXEDTEST_SIZE)) + cls.data = b'\n'.join(line_gen) + + def setUp(self): + self._limit = zipfile.ZIP64_LIMIT + self._filecount_limit = zipfile.ZIP_FILECOUNT_LIMIT + zipfile.ZIP64_LIMIT = 1000 + zipfile.ZIP_FILECOUNT_LIMIT = 9 + + # Make a source file with some lines + with open(TESTFN, "wb") as fp: + fp.write(self.data) + + def zip_test(self, f, compression): + # Create the ZIP archive + with zipfile.ZipFile(f, "w", compression, allowZip64=True) as zipfp: + zipfp.write(TESTFN, "another.name") + zipfp.write(TESTFN, TESTFN) + zipfp.writestr("strfile", self.data) + + # Read the ZIP archive + with zipfile.ZipFile(f, "r", compression) as zipfp: + self.assertEqual(zipfp.read(TESTFN), self.data) + self.assertEqual(zipfp.read("another.name"), self.data) + self.assertEqual(zipfp.read("strfile"), self.data) + + # Print the ZIP directory + fp = io.StringIO() + zipfp.printdir(fp) + + directory = fp.getvalue() + lines = directory.splitlines() + self.assertEqual(len(lines), 4) # Number of files + header + + self.assertIn('File Name', lines[0]) + self.assertIn('Modified', lines[0]) + self.assertIn('Size', lines[0]) + + fn, date, time_, size = lines[1].split() + self.assertEqual(fn, 'another.name') + self.assertTrue(time.strptime(date, '%Y-%m-%d')) + self.assertTrue(time.strptime(time_, '%H:%M:%S')) + self.assertEqual(size, str(len(self.data))) + + # Check the namelist + names = zipfp.namelist() + self.assertEqual(len(names), 3) + self.assertIn(TESTFN, names) + self.assertIn("another.name", names) + self.assertIn("strfile", names) + + # Check infolist + infos = zipfp.infolist() + names = [i.filename for i in infos] + self.assertEqual(len(names), 3) + self.assertIn(TESTFN, names) + self.assertIn("another.name", names) + self.assertIn("strfile", names) + for i in infos: + self.assertEqual(i.file_size, len(self.data)) + + # check getinfo + for nm in (TESTFN, "another.name", "strfile"): + info = zipfp.getinfo(nm) + self.assertEqual(info.filename, nm) + self.assertEqual(info.file_size, len(self.data)) + + # Check that testzip thinks the archive is valid + self.assertIsNone(zipfp.testzip()) + + def test_basic(self): + for f in get_files(self): + self.zip_test(f, self.compression) + + def test_too_many_files(self): + # This test checks that more than 64k files can be added to an archive, + # and that the resulting archive can be read properly by ZipFile + zipf = zipfile.ZipFile(TESTFN, "w", self.compression, + allowZip64=True) + zipf.debug = 100 + numfiles = 15 + for i in range(numfiles): + zipf.writestr("foo%08d" % i, "%d" % (i**3 % 57)) + self.assertEqual(len(zipf.namelist()), numfiles) + zipf.close() + + zipf2 = zipfile.ZipFile(TESTFN, "r", self.compression) + self.assertEqual(len(zipf2.namelist()), numfiles) + for i in range(numfiles): + content = zipf2.read("foo%08d" % i).decode('ascii') + self.assertEqual(content, "%d" % (i**3 % 57)) + zipf2.close() + + def test_too_many_files_append(self): + zipf = zipfile.ZipFile(TESTFN, "w", self.compression, + allowZip64=False) + zipf.debug = 100 + numfiles = 9 + for i in range(numfiles): + zipf.writestr("foo%08d" % i, "%d" % (i**3 % 57)) + self.assertEqual(len(zipf.namelist()), numfiles) + with self.assertRaises(zipfile.LargeZipFile): + zipf.writestr("foo%08d" % numfiles, b'') + self.assertEqual(len(zipf.namelist()), numfiles) + zipf.close() + + zipf = zipfile.ZipFile(TESTFN, "a", self.compression, + allowZip64=False) + zipf.debug = 100 + self.assertEqual(len(zipf.namelist()), numfiles) + with self.assertRaises(zipfile.LargeZipFile): + zipf.writestr("foo%08d" % numfiles, b'') + self.assertEqual(len(zipf.namelist()), numfiles) + zipf.close() + + zipf = zipfile.ZipFile(TESTFN, "a", self.compression, + allowZip64=True) + zipf.debug = 100 + self.assertEqual(len(zipf.namelist()), numfiles) + numfiles2 = 15 + for i in range(numfiles, numfiles2): + zipf.writestr("foo%08d" % i, "%d" % (i**3 % 57)) + self.assertEqual(len(zipf.namelist()), numfiles2) + zipf.close() + + zipf2 = zipfile.ZipFile(TESTFN, "r", self.compression) + self.assertEqual(len(zipf2.namelist()), numfiles2) + for i in range(numfiles2): + content = zipf2.read("foo%08d" % i).decode('ascii') + self.assertEqual(content, "%d" % (i**3 % 57)) + zipf2.close() + + def tearDown(self): + zipfile.ZIP64_LIMIT = self._limit + zipfile.ZIP_FILECOUNT_LIMIT = self._filecount_limit + unlink(TESTFN) + unlink(TESTFN2) + + +class StoredTestZip64InSmallFiles(AbstractTestZip64InSmallFiles, + unittest.TestCase): + compression = zipfile.ZIP_STORED + + def large_file_exception_test(self, f, compression): + with zipfile.ZipFile(f, "w", compression, allowZip64=False) as zipfp: + self.assertRaises(zipfile.LargeZipFile, + zipfp.write, TESTFN, "another.name") + + def large_file_exception_test2(self, f, compression): + with zipfile.ZipFile(f, "w", compression, allowZip64=False) as zipfp: + self.assertRaises(zipfile.LargeZipFile, + zipfp.writestr, "another.name", self.data) + + def test_large_file_exception(self): + for f in get_files(self): + self.large_file_exception_test(f, zipfile.ZIP_STORED) + self.large_file_exception_test2(f, zipfile.ZIP_STORED) + + def test_absolute_arcnames(self): + with zipfile.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED, + allowZip64=True) as zipfp: + zipfp.write(TESTFN, "/absolute") + + with zipfile.ZipFile(TESTFN2, "r", zipfile.ZIP_STORED) as zipfp: + self.assertEqual(zipfp.namelist(), ["absolute"]) + + def test_append(self): + # Test that appending to the Zip64 archive doesn't change + # extra fields of existing entries. + with zipfile.ZipFile(TESTFN2, "w", allowZip64=True) as zipfp: + zipfp.writestr("strfile", self.data) + with zipfile.ZipFile(TESTFN2, "r", allowZip64=True) as zipfp: + zinfo = zipfp.getinfo("strfile") + extra = zinfo.extra + with zipfile.ZipFile(TESTFN2, "a", allowZip64=True) as zipfp: + zipfp.writestr("strfile2", self.data) + with zipfile.ZipFile(TESTFN2, "r", allowZip64=True) as zipfp: + zinfo = zipfp.getinfo("strfile") + self.assertEqual(zinfo.extra, extra) + + def make_zip64_file( + self, file_size_64_set=False, file_size_extra=False, + compress_size_64_set=False, compress_size_extra=False, + header_offset_64_set=False, header_offset_extra=False, + ): + """Generate bytes sequence for a zip with (incomplete) zip64 data. + + The actual values (not the zip 64 0xffffffff values) stored in the file + are: + file_size: 8 + compress_size: 8 + header_offset: 0 + """ + actual_size = 8 + actual_header_offset = 0 + local_zip64_fields = [] + central_zip64_fields = [] + + file_size = actual_size + if file_size_64_set: + file_size = 0xffffffff + if file_size_extra: + local_zip64_fields.append(actual_size) + central_zip64_fields.append(actual_size) + file_size = struct.pack("= ZIP64_VERSION + # - The compressed and uncompressed size in the file headers are both + # 0xFFFFFFFF (ie. point to zip64 record) + # - The zip64 record is provided and has the correct sizes in it + # Other aspects of the zip are checked as well, but verifying the above is the main goal. + # Because this is hard to verify by parsing the data as a zip, the raw + # bytes are checked to ensure that they line up with the zip spec. + # The spec for this can be found at: https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT + # The relevent sections for this test are: + # - 4.3.7 for local file header + # - 4.5.3 for zip64 extra field + + data = io.BytesIO() + with zipfile.ZipFile(data, mode="w", allowZip64=True) as zf: + with zf.open("text.txt", mode="w", force_zip64=True) as zi: + zi.write(b"_") + + zipdata = data.getvalue() + + # pull out and check zip information + ( + header, vers, os, flags, comp, csize, usize, fn_len, + ex_total_len, filename, ex_id, ex_len, ex_usize, ex_csize, cd_sig + ) = struct.unpack("<4sBBHH8xIIHH8shhQQx4s", zipdata[:63]) + + self.assertEqual(header, b"PK\x03\x04") # local file header + self.assertGreaterEqual(vers, zipfile.ZIP64_VERSION) # requires zip64 to extract + self.assertEqual(os, 0) # compatible with MS-DOS + self.assertEqual(flags, 0) # no flags + self.assertEqual(comp, 0) # compression method = stored + self.assertEqual(csize, 0xFFFFFFFF) # sizes are in zip64 extra + self.assertEqual(usize, 0xFFFFFFFF) + self.assertEqual(fn_len, 8) # filename len + self.assertEqual(ex_total_len, 20) # size of extra records + self.assertEqual(ex_id, 1) # Zip64 extra record + self.assertEqual(ex_len, 16) # 16 bytes of data + self.assertEqual(ex_usize, 1) # uncompressed size + self.assertEqual(ex_csize, 1) # compressed size + self.assertEqual(cd_sig, b"PK\x01\x02") # ensure the central directory header is next + + z = zipfile.ZipFile(io.BytesIO(zipdata)) + zinfos = z.infolist() + self.assertEqual(len(zinfos), 1) + self.assertGreaterEqual(zinfos[0].extract_version, zipfile.ZIP64_VERSION) # requires zip64 to extract + + def test_unseekable_zip_unknown_filesize(self): + """Test that creating a zip with/without seeking will raise a RuntimeError if zip64 was required but not used""" + + def make_zip(fp): + with zipfile.ZipFile(fp, mode="w", allowZip64=True) as zf: + with zf.open("text.txt", mode="w", force_zip64=False) as zi: + zi.write(b"_" * (zipfile.ZIP64_LIMIT + 1)) + + self.assertRaises(RuntimeError, make_zip, io.BytesIO()) + self.assertRaises(RuntimeError, make_zip, Unseekable(io.BytesIO())) + + def test_zip64_required_not_allowed_fail(self): + """Test that trying to add a large file to a zip that doesn't allow zip64 extensions fails on add""" + def make_zip(fp): + with zipfile.ZipFile(fp, mode="w", allowZip64=False) as zf: + # pretend zipfile.ZipInfo.from_file was used to get the name and filesize + info = zipfile.ZipInfo("text.txt") + info.file_size = zipfile.ZIP64_LIMIT + 1 + zf.open(info, mode="w") + + self.assertRaises(zipfile.LargeZipFile, make_zip, io.BytesIO()) + self.assertRaises(zipfile.LargeZipFile, make_zip, Unseekable(io.BytesIO())) + + def test_unseekable_zip_known_filesize(self): + """Test that creating a zip without seeking will use zip64 extensions if the file size is provided up-front""" + + # This test ensures that the zip will use a zip64 data descriptor (same + # as a regular data descriptor except the sizes are 8 bytes instead of + # 4) record to communicate the size of a file if the zip is being + # written to an unseekable stream. + # Because this sort of thing is hard to verify by parsing the data back + # in as a zip, this test looks at the raw bytes created to ensure that + # the correct data has been generated. + # The spec for this can be found at: https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT + # The relevent sections for this test are: + # - 4.3.7 for local file header + # - 4.3.9 for the data descriptor + # - 4.5.3 for zip64 extra field + + file_size = zipfile.ZIP64_LIMIT + 1 + + def make_zip(fp): + with zipfile.ZipFile(fp, mode="w", allowZip64=True) as zf: + # pretend zipfile.ZipInfo.from_file was used to get the name and filesize + info = zipfile.ZipInfo("text.txt") + info.file_size = file_size + with zf.open(info, mode="w", force_zip64=False) as zi: + zi.write(b"_" * file_size) + return fp + + # check seekable file information + seekable_data = make_zip(io.BytesIO()).getvalue() + ( + header, vers, os, flags, comp, csize, usize, fn_len, + ex_total_len, filename, ex_id, ex_len, ex_usize, ex_csize, + cd_sig + ) = struct.unpack("<4sBBHH8xIIHH8shhQQ{}x4s".format(file_size), seekable_data[:62 + file_size]) + + self.assertEqual(header, b"PK\x03\x04") # local file header + self.assertGreaterEqual(vers, zipfile.ZIP64_VERSION) # requires zip64 to extract + self.assertEqual(os, 0) # compatible with MS-DOS + self.assertEqual(flags, 0) # no flags set + self.assertEqual(comp, 0) # compression method = stored + self.assertEqual(csize, 0xFFFFFFFF) # sizes are in zip64 extra + self.assertEqual(usize, 0xFFFFFFFF) + self.assertEqual(fn_len, 8) # filename len + self.assertEqual(ex_total_len, 20) # size of extra records + self.assertEqual(ex_id, 1) # Zip64 extra record + self.assertEqual(ex_len, 16) # 16 bytes of data + self.assertEqual(ex_usize, file_size) # uncompressed size + self.assertEqual(ex_csize, file_size) # compressed size + self.assertEqual(cd_sig, b"PK\x01\x02") # ensure the central directory header is next + + # check unseekable file information + unseekable_data = make_zip(Unseekable(io.BytesIO())).fp.getvalue() + ( + header, vers, os, flags, comp, csize, usize, fn_len, + ex_total_len, filename, ex_id, ex_len, ex_usize, ex_csize, + dd_header, dd_usize, dd_csize, cd_sig + ) = struct.unpack("<4sBBHH8xIIHH8shhQQ{}x4s4xQQ4s".format(file_size), unseekable_data[:86 + file_size]) + + self.assertEqual(header, b"PK\x03\x04") # local file header + self.assertGreaterEqual(vers, zipfile.ZIP64_VERSION) # requires zip64 to extract + self.assertEqual(os, 0) # compatible with MS-DOS + self.assertEqual("{:b}".format(flags), "1000") # streaming flag set + self.assertEqual(comp, 0) # compression method = stored + self.assertEqual(csize, 0xFFFFFFFF) # sizes are in zip64 extra + self.assertEqual(usize, 0xFFFFFFFF) + self.assertEqual(fn_len, 8) # filename len + self.assertEqual(ex_total_len, 20) # size of extra records + self.assertEqual(ex_id, 1) # Zip64 extra record + self.assertEqual(ex_len, 16) # 16 bytes of data + self.assertEqual(ex_usize, 0) # uncompressed size - 0 to defer to data descriptor + self.assertEqual(ex_csize, 0) # compressed size - 0 to defer to data descriptor + self.assertEqual(dd_header, b"PK\07\x08") # data descriptor + self.assertEqual(dd_usize, file_size) # file size (8 bytes because zip64) + self.assertEqual(dd_csize, file_size) # compressed size (8 bytes because zip64) + self.assertEqual(cd_sig, b"PK\x01\x02") # ensure the central directory header is next + + +@requires_zlib() +class DeflateTestZip64InSmallFiles(AbstractTestZip64InSmallFiles, + unittest.TestCase): + compression = zipfile.ZIP_DEFLATED + +@requires_bz2() +class Bzip2TestZip64InSmallFiles(AbstractTestZip64InSmallFiles, + unittest.TestCase): + compression = zipfile.ZIP_BZIP2 + +@requires_lzma() +class LzmaTestZip64InSmallFiles(AbstractTestZip64InSmallFiles, + unittest.TestCase): + compression = zipfile.ZIP_LZMA + + +class AbstractWriterTests: + + def tearDown(self): + unlink(TESTFN2) + + def test_close_after_close(self): + data = b'content' + with zipfile.ZipFile(TESTFN2, "w", self.compression) as zipf: + w = zipf.open('test', 'w') + w.write(data) + w.close() + self.assertTrue(w.closed) + w.close() + self.assertTrue(w.closed) + self.assertEqual(zipf.read('test'), data) + + def test_write_after_close(self): + data = b'content' + with zipfile.ZipFile(TESTFN2, "w", self.compression) as zipf: + w = zipf.open('test', 'w') + w.write(data) + w.close() + self.assertTrue(w.closed) + self.assertRaises(ValueError, w.write, b'') + self.assertEqual(zipf.read('test'), data) + + def test_issue44439(self): + q = array.array('Q', [1, 2, 3, 4, 5]) + LENGTH = len(q) * q.itemsize + with zipfile.ZipFile(io.BytesIO(), 'w', self.compression) as zip: + with zip.open('data', 'w') as data: + self.assertEqual(data.write(q), LENGTH) + self.assertEqual(zip.getinfo('data').file_size, LENGTH) + + def test_zipwritefile_attrs(self): + fname = "somefile.txt" + with zipfile.ZipFile(TESTFN2, mode="w", compression=self.compression) as zipfp: + with zipfp.open(fname, 'w') as fid: + self.assertRaises(io.UnsupportedOperation, fid.fileno) + self.assertIs(fid.readable(), False) + self.assertIs(fid.writable(), True) + self.assertIs(fid.seekable(), False) + self.assertIs(fid.closed, False) + self.assertIs(fid.closed, True) + self.assertRaises(io.UnsupportedOperation, fid.fileno) + self.assertIs(fid.readable(), False) + self.assertIs(fid.writable(), True) + self.assertIs(fid.seekable(), False) + +class StoredWriterTests(AbstractWriterTests, unittest.TestCase): + compression = zipfile.ZIP_STORED + +@requires_zlib() +class DeflateWriterTests(AbstractWriterTests, unittest.TestCase): + compression = zipfile.ZIP_DEFLATED + +@requires_bz2() +class Bzip2WriterTests(AbstractWriterTests, unittest.TestCase): + compression = zipfile.ZIP_BZIP2 + +@requires_lzma() +class LzmaWriterTests(AbstractWriterTests, unittest.TestCase): + compression = zipfile.ZIP_LZMA + + +class PyZipFileTests(unittest.TestCase): + def assertCompiledIn(self, name, namelist): + if name + 'o' not in namelist: + self.assertIn(name + 'c', namelist) + + def requiresWriteAccess(self, path): + # effective_ids unavailable on windows + if not os.access(path, os.W_OK, + effective_ids=os.access in os.supports_effective_ids): + self.skipTest('requires write access to the installed location') + filename = os.path.join(path, 'test_zipfile.try') + try: + fd = os.open(filename, os.O_WRONLY | os.O_CREAT) + os.close(fd) + except Exception: + self.skipTest('requires write access to the installed location') + unlink(filename) + + def test_write_pyfile(self): + self.requiresWriteAccess(os.path.dirname(__file__)) + with TemporaryFile() as t, zipfile.PyZipFile(t, "w") as zipfp: + fn = __file__ + if fn.endswith('.pyc'): + path_split = fn.split(os.sep) + if os.altsep is not None: + path_split.extend(fn.split(os.altsep)) + if '__pycache__' in path_split: + fn = importlib.util.source_from_cache(fn) + else: + fn = fn[:-1] + + zipfp.writepy(fn) + + bn = os.path.basename(fn) + self.assertNotIn(bn, zipfp.namelist()) + self.assertCompiledIn(bn, zipfp.namelist()) + + with TemporaryFile() as t, zipfile.PyZipFile(t, "w") as zipfp: + fn = __file__ + if fn.endswith('.pyc'): + fn = fn[:-1] + + zipfp.writepy(fn, "testpackage") + + bn = "%s/%s" % ("testpackage", os.path.basename(fn)) + self.assertNotIn(bn, zipfp.namelist()) + self.assertCompiledIn(bn, zipfp.namelist()) + + def test_write_python_package(self): + import email + packagedir = os.path.dirname(email.__file__) + self.requiresWriteAccess(packagedir) + + with TemporaryFile() as t, zipfile.PyZipFile(t, "w") as zipfp: + zipfp.writepy(packagedir) + + # Check for a couple of modules at different levels of the + # hierarchy + names = zipfp.namelist() + self.assertCompiledIn('email/__init__.py', names) + self.assertCompiledIn('email/mime/text.py', names) + + def test_write_filtered_python_package(self): + import test + packagedir = os.path.dirname(test.__file__) + self.requiresWriteAccess(packagedir) + + with TemporaryFile() as t, zipfile.PyZipFile(t, "w") as zipfp: + + # first make sure that the test folder gives error messages + # (on the badsyntax_... files) + with captured_stdout() as reportSIO: + zipfp.writepy(packagedir) + reportStr = reportSIO.getvalue() + self.assertTrue('SyntaxError' in reportStr) + + # then check that the filter works on the whole package + with captured_stdout() as reportSIO: + zipfp.writepy(packagedir, filterfunc=lambda whatever: False) + reportStr = reportSIO.getvalue() + self.assertTrue('SyntaxError' not in reportStr) + + # then check that the filter works on individual files + def filter(path): + return not os.path.basename(path).startswith("bad") + with captured_stdout() as reportSIO, self.assertWarns(UserWarning): + zipfp.writepy(packagedir, filterfunc=filter) + reportStr = reportSIO.getvalue() + if reportStr: + print(reportStr) + self.assertTrue('SyntaxError' not in reportStr) + + def test_write_with_optimization(self): + import email + packagedir = os.path.dirname(email.__file__) + self.requiresWriteAccess(packagedir) + optlevel = 1 if __debug__ else 0 + ext = '.pyc' + + with TemporaryFile() as t, \ + zipfile.PyZipFile(t, "w", optimize=optlevel) as zipfp: + zipfp.writepy(packagedir) + + names = zipfp.namelist() + self.assertIn('email/__init__' + ext, names) + self.assertIn('email/mime/text' + ext, names) + + def test_write_python_directory(self): + os.mkdir(TESTFN2) + try: + with open(os.path.join(TESTFN2, "mod1.py"), "w", encoding='utf-8') as fp: + fp.write("print(42)\n") + + with open(os.path.join(TESTFN2, "mod2.py"), "w", encoding='utf-8') as fp: + fp.write("print(42 * 42)\n") + + with open(os.path.join(TESTFN2, "mod2.txt"), "w", encoding='utf-8') as fp: + fp.write("bla bla bla\n") + + with TemporaryFile() as t, zipfile.PyZipFile(t, "w") as zipfp: + zipfp.writepy(TESTFN2) + + names = zipfp.namelist() + self.assertCompiledIn('mod1.py', names) + self.assertCompiledIn('mod2.py', names) + self.assertNotIn('mod2.txt', names) + + finally: + rmtree(TESTFN2) + + def test_write_python_directory_filtered(self): + os.mkdir(TESTFN2) + try: + with open(os.path.join(TESTFN2, "mod1.py"), "w", encoding='utf-8') as fp: + fp.write("print(42)\n") + + with open(os.path.join(TESTFN2, "mod2.py"), "w", encoding='utf-8') as fp: + fp.write("print(42 * 42)\n") + + with TemporaryFile() as t, zipfile.PyZipFile(t, "w") as zipfp: + zipfp.writepy(TESTFN2, filterfunc=lambda fn: + not fn.endswith('mod2.py')) + + names = zipfp.namelist() + self.assertCompiledIn('mod1.py', names) + self.assertNotIn('mod2.py', names) + + finally: + rmtree(TESTFN2) + + def test_write_non_pyfile(self): + with TemporaryFile() as t, zipfile.PyZipFile(t, "w") as zipfp: + with open(TESTFN, 'w', encoding='utf-8') as f: + f.write('most definitely not a python file') + self.assertRaises(RuntimeError, zipfp.writepy, TESTFN) + unlink(TESTFN) + + def test_write_pyfile_bad_syntax(self): + os.mkdir(TESTFN2) + try: + with open(os.path.join(TESTFN2, "mod1.py"), "w", encoding='utf-8') as fp: + fp.write("Bad syntax in python file\n") + + with TemporaryFile() as t, zipfile.PyZipFile(t, "w") as zipfp: + # syntax errors are printed to stdout + with captured_stdout() as s: + zipfp.writepy(os.path.join(TESTFN2, "mod1.py")) + + self.assertIn("SyntaxError", s.getvalue()) + + # as it will not have compiled the python file, it will + # include the .py file not .pyc + names = zipfp.namelist() + self.assertIn('mod1.py', names) + self.assertNotIn('mod1.pyc', names) + + finally: + rmtree(TESTFN2) + + def test_write_pathlike(self): + os.mkdir(TESTFN2) + try: + with open(os.path.join(TESTFN2, "mod1.py"), "w", encoding='utf-8') as fp: + fp.write("print(42)\n") + + with TemporaryFile() as t, zipfile.PyZipFile(t, "w") as zipfp: + zipfp.writepy(FakePath(os.path.join(TESTFN2, "mod1.py"))) + names = zipfp.namelist() + self.assertCompiledIn('mod1.py', names) + finally: + rmtree(TESTFN2) + + +class ExtractTests(unittest.TestCase): + + def make_test_file(self): + with zipfile.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED) as zipfp: + for fpath, fdata in SMALL_TEST_DATA: + zipfp.writestr(fpath, fdata) + + def test_extract(self): + with temp_cwd(): + self.make_test_file() + with zipfile.ZipFile(TESTFN2, "r") as zipfp: + for fpath, fdata in SMALL_TEST_DATA: + writtenfile = zipfp.extract(fpath) + + # make sure it was written to the right place + correctfile = os.path.join(os.getcwd(), fpath) + correctfile = os.path.normpath(correctfile) + + self.assertEqual(writtenfile, correctfile) + + # make sure correct data is in correct file + with open(writtenfile, "rb") as f: + self.assertEqual(fdata.encode(), f.read()) + + unlink(writtenfile) + + def _test_extract_with_target(self, target): + self.make_test_file() + with zipfile.ZipFile(TESTFN2, "r") as zipfp: + for fpath, fdata in SMALL_TEST_DATA: + writtenfile = zipfp.extract(fpath, target) + + # make sure it was written to the right place + correctfile = os.path.join(target, fpath) + correctfile = os.path.normpath(correctfile) + self.assertTrue(os.path.samefile(writtenfile, correctfile), (writtenfile, target)) + + # make sure correct data is in correct file + with open(writtenfile, "rb") as f: + self.assertEqual(fdata.encode(), f.read()) + + unlink(writtenfile) + + unlink(TESTFN2) + + def test_extract_with_target(self): + with temp_dir() as extdir: + self._test_extract_with_target(extdir) + + def test_extract_with_target_pathlike(self): + with temp_dir() as extdir: + self._test_extract_with_target(FakePath(extdir)) + + def test_extract_all(self): + with temp_cwd(): + self.make_test_file() + with zipfile.ZipFile(TESTFN2, "r") as zipfp: + zipfp.extractall() + for fpath, fdata in SMALL_TEST_DATA: + outfile = os.path.join(os.getcwd(), fpath) + + with open(outfile, "rb") as f: + self.assertEqual(fdata.encode(), f.read()) + + unlink(outfile) + + def _test_extract_all_with_target(self, target): + self.make_test_file() + with zipfile.ZipFile(TESTFN2, "r") as zipfp: + zipfp.extractall(target) + for fpath, fdata in SMALL_TEST_DATA: + outfile = os.path.join(target, fpath) + + with open(outfile, "rb") as f: + self.assertEqual(fdata.encode(), f.read()) + + unlink(outfile) + + unlink(TESTFN2) + + def test_extract_all_with_target(self): + with temp_dir() as extdir: + self._test_extract_all_with_target(extdir) + + def test_extract_all_with_target_pathlike(self): + with temp_dir() as extdir: + self._test_extract_all_with_target(FakePath(extdir)) + + def check_file(self, filename, content): + self.assertTrue(os.path.isfile(filename)) + with open(filename, 'rb') as f: + self.assertEqual(f.read(), content) + + def test_sanitize_windows_name(self): + san = zipfile.ZipFile._sanitize_windows_name + # Passing pathsep in allows this test to work regardless of platform. + self.assertEqual(san(r',,?,C:,foo,bar/z', ','), r'_,C_,foo,bar/z') + self.assertEqual(san(r'a\b,ce|f"g?h*i', ','), r'a\b,c_d_e_f_g_h_i') + self.assertEqual(san('../../foo../../ba..r', '/'), r'foo/ba..r') + self.assertEqual(san(' / /foo / /ba r', '/'), r'foo/ba r') + self.assertEqual(san(' . /. /foo ./ . /. ./ba .r', '/'), r'foo/ba .r') + + def test_extract_hackers_arcnames_common_cases(self): + common_hacknames = [ + ('../foo/bar', 'foo/bar'), + ('foo/../bar', 'foo/bar'), + ('foo/../../bar', 'foo/bar'), + ('foo/bar/..', 'foo/bar'), + ('./../foo/bar', 'foo/bar'), + ('/foo/bar', 'foo/bar'), + ('/foo/../bar', 'foo/bar'), + ('/foo/../../bar', 'foo/bar'), + ] + self._test_extract_hackers_arcnames(common_hacknames) + + @unittest.skipIf(os.path.sep != '\\', 'Requires \\ as path separator.') + def test_extract_hackers_arcnames_windows_only(self): + """Test combination of path fixing and windows name sanitization.""" + windows_hacknames = [ + (r'..\foo\bar', 'foo/bar'), + (r'..\/foo\/bar', 'foo/bar'), + (r'foo/\..\/bar', 'foo/bar'), + (r'foo\/../\bar', 'foo/bar'), + (r'C:foo/bar', 'foo/bar'), + (r'C:/foo/bar', 'foo/bar'), + (r'C://foo/bar', 'foo/bar'), + (r'C:\foo\bar', 'foo/bar'), + (r'//conky/mountpoint/foo/bar', 'foo/bar'), + (r'\\conky\mountpoint\foo\bar', 'foo/bar'), + (r'///conky/mountpoint/foo/bar', 'mountpoint/foo/bar'), + (r'\\\conky\mountpoint\foo\bar', 'mountpoint/foo/bar'), + (r'//conky//mountpoint/foo/bar', 'mountpoint/foo/bar'), + (r'\\conky\\mountpoint\foo\bar', 'mountpoint/foo/bar'), + (r'//?/C:/foo/bar', 'foo/bar'), + (r'\\?\C:\foo\bar', 'foo/bar'), + (r'C:/../C:/foo/bar', 'C_/foo/bar'), + (r'a:b\ce|f"g?h*i', 'b/c_d_e_f_g_h_i'), + ('../../foo../../ba..r', 'foo/ba..r'), + ] + self._test_extract_hackers_arcnames(windows_hacknames) + + @unittest.skipIf(os.path.sep != '/', r'Requires / as path separator.') + def test_extract_hackers_arcnames_posix_only(self): + posix_hacknames = [ + ('//foo/bar', 'foo/bar'), + ('../../foo../../ba..r', 'foo../ba..r'), + (r'foo/..\bar', r'foo/..\bar'), + ] + self._test_extract_hackers_arcnames(posix_hacknames) + + def _test_extract_hackers_arcnames(self, hacknames): + for arcname, fixedname in hacknames: + content = b'foobar' + arcname.encode() + with zipfile.ZipFile(TESTFN2, 'w', zipfile.ZIP_STORED) as zipfp: + zinfo = zipfile.ZipInfo() + # preserve backslashes + zinfo.filename = arcname + zinfo.external_attr = 0o600 << 16 + zipfp.writestr(zinfo, content) + + arcname = arcname.replace(os.sep, "/") + targetpath = os.path.join('target', 'subdir', 'subsub') + correctfile = os.path.join(targetpath, *fixedname.split('/')) + + with zipfile.ZipFile(TESTFN2, 'r') as zipfp: + writtenfile = zipfp.extract(arcname, targetpath) + self.assertEqual(writtenfile, correctfile, + msg='extract %r: %r != %r' % + (arcname, writtenfile, correctfile)) + self.check_file(correctfile, content) + rmtree('target') + + with zipfile.ZipFile(TESTFN2, 'r') as zipfp: + zipfp.extractall(targetpath) + self.check_file(correctfile, content) + rmtree('target') + + correctfile = os.path.join(os.getcwd(), *fixedname.split('/')) + + with zipfile.ZipFile(TESTFN2, 'r') as zipfp: + writtenfile = zipfp.extract(arcname) + self.assertEqual(writtenfile, correctfile, + msg="extract %r" % arcname) + self.check_file(correctfile, content) + rmtree(fixedname.split('/')[0]) + + with zipfile.ZipFile(TESTFN2, 'r') as zipfp: + zipfp.extractall() + self.check_file(correctfile, content) + rmtree(fixedname.split('/')[0]) + + unlink(TESTFN2) + + +class OverwriteTests(archiver_tests.OverwriteTests, unittest.TestCase): + testdir = TESTFN + + @classmethod + def setUpClass(cls): + p = cls.ar_with_file = TESTFN + '-with-file.zip' + cls.addClassCleanup(unlink, p) + with zipfile.ZipFile(p, 'w') as zipfp: + zipfp.writestr('test', b'newcontent') + + p = cls.ar_with_dir = TESTFN + '-with-dir.zip' + cls.addClassCleanup(unlink, p) + with zipfile.ZipFile(p, 'w') as zipfp: + zipfp.mkdir('test') + + p = cls.ar_with_implicit_dir = TESTFN + '-with-implicit-dir.zip' + cls.addClassCleanup(unlink, p) + with zipfile.ZipFile(p, 'w') as zipfp: + zipfp.writestr('test/file', b'newcontent') + + def open(self, path): + return zipfile.ZipFile(path, 'r') + + def extractall(self, ar): + ar.extractall(self.testdir) + + +class OtherTests(unittest.TestCase): + def test_open_via_zip_info(self): + # Create the ZIP archive + with zipfile.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED) as zipfp: + zipfp.writestr("name", "foo") + with self.assertWarns(UserWarning): + zipfp.writestr("name", "bar") + self.assertEqual(zipfp.namelist(), ["name"] * 2) + + with zipfile.ZipFile(TESTFN2, "r") as zipfp: + infos = zipfp.infolist() + data = b"" + for info in infos: + with zipfp.open(info) as zipopen: + data += zipopen.read() + self.assertIn(data, {b"foobar", b"barfoo"}) + data = b"" + for info in infos: + data += zipfp.read(info) + self.assertIn(data, {b"foobar", b"barfoo"}) + + def test_writestr_extended_local_header_issue1202(self): + with zipfile.ZipFile(TESTFN2, 'w') as orig_zip: + for data in 'abcdefghijklmnop': + zinfo = zipfile.ZipInfo(data) + zinfo.flag_bits |= zipfile._MASK_USE_DATA_DESCRIPTOR # Include an extended local header. + orig_zip.writestr(zinfo, data) + + def test_close(self): + """Check that the zipfile is closed after the 'with' block.""" + with zipfile.ZipFile(TESTFN2, "w") as zipfp: + for fpath, fdata in SMALL_TEST_DATA: + zipfp.writestr(fpath, fdata) + self.assertIsNotNone(zipfp.fp, 'zipfp is not open') + self.assertIsNone(zipfp.fp, 'zipfp is not closed') + + with zipfile.ZipFile(TESTFN2, "r") as zipfp: + self.assertIsNotNone(zipfp.fp, 'zipfp is not open') + self.assertIsNone(zipfp.fp, 'zipfp is not closed') + + def test_close_on_exception(self): + """Check that the zipfile is closed if an exception is raised in the + 'with' block.""" + with zipfile.ZipFile(TESTFN2, "w") as zipfp: + for fpath, fdata in SMALL_TEST_DATA: + zipfp.writestr(fpath, fdata) + + try: + with zipfile.ZipFile(TESTFN2, "r") as zipfp2: + raise zipfile.BadZipFile() + except zipfile.BadZipFile: + self.assertIsNone(zipfp2.fp, 'zipfp is not closed') + + def test_unsupported_version(self): + # File has an extract_version of 120 + data = (b'PK\x03\x04x\x00\x00\x00\x00\x00!p\xa1@\x00\x00\x00\x00\x00\x00' + b'\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00xPK\x01\x02x\x03x\x00\x00\x00\x00' + b'\x00!p\xa1@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00' + b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x01\x00\x00\x00\x00xPK\x05\x06' + b'\x00\x00\x00\x00\x01\x00\x01\x00/\x00\x00\x00\x1f\x00\x00\x00\x00\x00') + + self.assertRaises(NotImplementedError, zipfile.ZipFile, + io.BytesIO(data), 'r') + + @requires_zlib() + def test_read_unicode_filenames(self): + # bug #10801 + fname = findfile('zip_cp437_header.zip') + with zipfile.ZipFile(fname) as zipfp: + for name in zipfp.namelist(): + zipfp.open(name).close() + + def test_write_unicode_filenames(self): + with zipfile.ZipFile(TESTFN, "w") as zf: + zf.writestr("foo.txt", "Test for unicode filename") + zf.writestr("\xf6.txt", "Test for unicode filename") + self.assertIsInstance(zf.infolist()[0].filename, str) + + with zipfile.ZipFile(TESTFN, "r") as zf: + self.assertEqual(zf.filelist[0].filename, "foo.txt") + self.assertEqual(zf.filelist[1].filename, "\xf6.txt") + + def create_zipfile_with_extra_data(self, filename, extra_data_name): + with zipfile.ZipFile(TESTFN, mode='w') as zf: + filename_encoded = filename.encode("utf-8") + # create a ZipInfo object with Unicode path extra field + zip_info = zipfile.ZipInfo(filename) + + tag_for_unicode_path = b'\x75\x70' + version_of_unicode_path = b'\x01' + + import zlib + filename_crc = struct.pack(' os.path.getsize(TESTFN)) + with zipfile.ZipFile(TESTFN,mode="r") as zipf: + self.assertEqual(zipf.comment, b"shorter comment") + + def test_unicode_comment(self): + with zipfile.ZipFile(TESTFN, "w", zipfile.ZIP_STORED) as zipf: + zipf.writestr("foo.txt", "O, for a Muse of Fire!") + with self.assertRaises(TypeError): + zipf.comment = "this is an error" + + def test_change_comment_in_empty_archive(self): + with zipfile.ZipFile(TESTFN, "a", zipfile.ZIP_STORED) as zipf: + self.assertFalse(zipf.filelist) + zipf.comment = b"this is a comment" + with zipfile.ZipFile(TESTFN, "r") as zipf: + self.assertEqual(zipf.comment, b"this is a comment") + + def test_change_comment_in_nonempty_archive(self): + with zipfile.ZipFile(TESTFN, "w", zipfile.ZIP_STORED) as zipf: + zipf.writestr("foo.txt", "O, for a Muse of Fire!") + with zipfile.ZipFile(TESTFN, "a", zipfile.ZIP_STORED) as zipf: + self.assertTrue(zipf.filelist) + zipf.comment = b"this is a comment" + with zipfile.ZipFile(TESTFN, "r") as zipf: + self.assertEqual(zipf.comment, b"this is a comment") + + def test_empty_zipfile(self): + # Check that creating a file in 'w' or 'a' mode and closing without + # adding any files to the archives creates a valid empty ZIP file + zipf = zipfile.ZipFile(TESTFN, mode="w") + zipf.close() + try: + zipf = zipfile.ZipFile(TESTFN, mode="r") + except zipfile.BadZipFile: + self.fail("Unable to create empty ZIP file in 'w' mode") + + zipf = zipfile.ZipFile(TESTFN, mode="a") + zipf.close() + try: + zipf = zipfile.ZipFile(TESTFN, mode="r") + except: + self.fail("Unable to create empty ZIP file in 'a' mode") + + def test_open_empty_file(self): + # Issue 1710703: Check that opening a file with less than 22 bytes + # raises a BadZipFile exception (rather than the previously unhelpful + # OSError) + f = open(TESTFN, 'w', encoding='utf-8') + f.close() + self.assertRaises(zipfile.BadZipFile, zipfile.ZipFile, TESTFN, 'r') + + def test_create_zipinfo_before_1980(self): + self.assertRaises(ValueError, + zipfile.ZipInfo, 'seventies', (1979, 1, 1, 0, 0, 0)) + + def test_create_empty_zipinfo_repr(self): + """Before bpo-26185, repr() on empty ZipInfo object was failing.""" + zi = zipfile.ZipInfo(filename="empty") + self.assertEqual(repr(zi), "") + + def test_create_empty_zipinfo_default_attributes(self): + """Ensure all required attributes are set.""" + zi = zipfile.ZipInfo() + self.assertEqual(zi.orig_filename, "NoName") + self.assertEqual(zi.filename, "NoName") + self.assertEqual(zi.date_time, (1980, 1, 1, 0, 0, 0)) + self.assertEqual(zi.compress_type, zipfile.ZIP_STORED) + self.assertEqual(zi.comment, b"") + self.assertEqual(zi.extra, b"") + self.assertIn(zi.create_system, (0, 3)) + self.assertEqual(zi.create_version, zipfile.DEFAULT_VERSION) + self.assertEqual(zi.extract_version, zipfile.DEFAULT_VERSION) + self.assertEqual(zi.reserved, 0) + self.assertEqual(zi.flag_bits, 0) + self.assertEqual(zi.volume, 0) + self.assertEqual(zi.internal_attr, 0) + self.assertEqual(zi.external_attr, 0) + + # Before bpo-26185, both were missing + self.assertEqual(zi.file_size, 0) + self.assertEqual(zi.compress_size, 0) + + def test_zipfile_with_short_extra_field(self): + """If an extra field in the header is less than 4 bytes, skip it.""" + zipdata = ( + b'PK\x03\x04\x14\x00\x00\x00\x00\x00\x93\x9b\xad@\x8b\x9e' + b'\xd9\xd3\x01\x00\x00\x00\x01\x00\x00\x00\x03\x00\x03\x00ab' + b'c\x00\x00\x00APK\x01\x02\x14\x03\x14\x00\x00\x00\x00' + b'\x00\x93\x9b\xad@\x8b\x9e\xd9\xd3\x01\x00\x00\x00\x01\x00\x00' + b'\x00\x03\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x00' + b'\x00\x00\x00abc\x00\x00PK\x05\x06\x00\x00\x00\x00' + b'\x01\x00\x01\x003\x00\x00\x00%\x00\x00\x00\x00\x00' + ) + with zipfile.ZipFile(io.BytesIO(zipdata), 'r') as zipf: + # testzip returns the name of the first corrupt file, or None + self.assertIsNone(zipf.testzip()) + + def test_open_conflicting_handles(self): + # It's only possible to open one writable file handle at a time + msg1 = b"It's fun to charter an accountant!" + msg2 = b"And sail the wide accountant sea" + msg3 = b"To find, explore the funds offshore" + with zipfile.ZipFile(TESTFN2, 'w', zipfile.ZIP_STORED) as zipf: + with zipf.open('foo', mode='w') as w2: + w2.write(msg1) + with zipf.open('bar', mode='w') as w1: + with self.assertRaises(ValueError): + zipf.open('handle', mode='w') + with self.assertRaises(ValueError): + zipf.open('foo', mode='r') + with self.assertRaises(ValueError): + zipf.writestr('str', 'abcde') + with self.assertRaises(ValueError): + zipf.write(__file__, 'file') + with self.assertRaises(ValueError): + zipf.close() + w1.write(msg2) + with zipf.open('baz', mode='w') as w2: + w2.write(msg3) + + with zipfile.ZipFile(TESTFN2, 'r') as zipf: + self.assertEqual(zipf.read('foo'), msg1) + self.assertEqual(zipf.read('bar'), msg2) + self.assertEqual(zipf.read('baz'), msg3) + self.assertEqual(zipf.namelist(), ['foo', 'bar', 'baz']) + + def test_seek_tell(self): + # Test seek functionality + txt = b"Where's Bruce?" + bloc = txt.find(b"Bruce") + # Check seek on a file + with zipfile.ZipFile(TESTFN, "w") as zipf: + zipf.writestr("foo.txt", txt) + with zipfile.ZipFile(TESTFN, "r") as zipf: + with zipf.open("foo.txt", "r") as fp: + fp.seek(bloc, os.SEEK_SET) + self.assertEqual(fp.tell(), bloc) + fp.seek(-bloc, os.SEEK_CUR) + self.assertEqual(fp.tell(), 0) + fp.seek(bloc, os.SEEK_CUR) + self.assertEqual(fp.tell(), bloc) + self.assertEqual(fp.read(5), txt[bloc:bloc+5]) + self.assertEqual(fp.tell(), bloc + 5) + fp.seek(0, os.SEEK_END) + self.assertEqual(fp.tell(), len(txt)) + fp.seek(0, os.SEEK_SET) + self.assertEqual(fp.tell(), 0) + # Check seek on memory file + data = io.BytesIO() + with zipfile.ZipFile(data, mode="w") as zipf: + zipf.writestr("foo.txt", txt) + with zipfile.ZipFile(data, mode="r") as zipf: + with zipf.open("foo.txt", "r") as fp: + fp.seek(bloc, os.SEEK_SET) + self.assertEqual(fp.tell(), bloc) + fp.seek(-bloc, os.SEEK_CUR) + self.assertEqual(fp.tell(), 0) + fp.seek(bloc, os.SEEK_CUR) + self.assertEqual(fp.tell(), bloc) + self.assertEqual(fp.read(5), txt[bloc:bloc+5]) + self.assertEqual(fp.tell(), bloc + 5) + fp.seek(0, os.SEEK_END) + self.assertEqual(fp.tell(), len(txt)) + fp.seek(0, os.SEEK_SET) + self.assertEqual(fp.tell(), 0) + + def test_read_after_seek(self): + # Issue 102956: Make sure seek(x, os.SEEK_CUR) doesn't break read() + txt = b"Charge men!" + bloc = txt.find(b"men") + with zipfile.ZipFile(TESTFN, "w") as zipf: + zipf.writestr("foo.txt", txt) + with zipfile.ZipFile(TESTFN, mode="r") as zipf: + with zipf.open("foo.txt", "r") as fp: + fp.seek(bloc, os.SEEK_CUR) + self.assertEqual(fp.read(-1), b'men!') + with zipfile.ZipFile(TESTFN, mode="r") as zipf: + with zipf.open("foo.txt", "r") as fp: + fp.read(6) + fp.seek(1, os.SEEK_CUR) + self.assertEqual(fp.read(-1), b'men!') + + @requires_bz2() + def test_decompress_without_3rd_party_library(self): + data = b'PK\x05\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + zip_file = io.BytesIO(data) + with zipfile.ZipFile(zip_file, 'w', compression=zipfile.ZIP_BZIP2) as zf: + zf.writestr('a.txt', b'a') + with mock.patch('zipfile.bz2', None): + with zipfile.ZipFile(zip_file) as zf: + self.assertRaises(RuntimeError, zf.extract, 'a.txt') + + @requires_zlib() + def test_full_overlap(self): + data = ( + b'PK\x03\x04\x14\x00\x00\x00\x08\x00\xa0lH\x05\xe2\x1e' + b'8\xbb\x10\x00\x00\x00\t\x04\x00\x00\x01\x00\x00\x00a\xed' + b'\xc0\x81\x08\x00\x00\x00\xc00\xd6\xfbK\\d\x0b`P' + b'K\x01\x02\x14\x00\x14\x00\x00\x00\x08\x00\xa0lH\x05\xe2' + b'\x1e8\xbb\x10\x00\x00\x00\t\x04\x00\x00\x01\x00\x00\x00\x00' + b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00aPK' + b'\x01\x02\x14\x00\x14\x00\x00\x00\x08\x00\xa0lH\x05\xe2\x1e' + b'8\xbb\x10\x00\x00\x00\t\x04\x00\x00\x01\x00\x00\x00\x00\x00' + b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00bPK\x05' + b'\x06\x00\x00\x00\x00\x02\x00\x02\x00^\x00\x00\x00/\x00\x00' + b'\x00\x00\x00' + ) + with zipfile.ZipFile(io.BytesIO(data), 'r') as zipf: + self.assertEqual(zipf.namelist(), ['a', 'b']) + zi = zipf.getinfo('a') + self.assertEqual(zi.header_offset, 0) + self.assertEqual(zi.compress_size, 16) + self.assertEqual(zi.file_size, 1033) + zi = zipf.getinfo('b') + self.assertEqual(zi.header_offset, 0) + self.assertEqual(zi.compress_size, 16) + self.assertEqual(zi.file_size, 1033) + self.assertEqual(len(zipf.read('a')), 1033) + with self.assertRaisesRegex(zipfile.BadZipFile, 'File name.*differ'): + zipf.read('b') + + @requires_zlib() + def test_quoted_overlap(self): + data = ( + b'PK\x03\x04\x14\x00\x00\x00\x08\x00\xa0lH\x05Y\xfc' + b'8\x044\x00\x00\x00(\x04\x00\x00\x01\x00\x00\x00a\x00' + b'\x1f\x00\xe0\xffPK\x03\x04\x14\x00\x00\x00\x08\x00\xa0l' + b'H\x05\xe2\x1e8\xbb\x10\x00\x00\x00\t\x04\x00\x00\x01\x00' + b'\x00\x00b\xed\xc0\x81\x08\x00\x00\x00\xc00\xd6\xfbK\\' + b'd\x0b`PK\x01\x02\x14\x00\x14\x00\x00\x00\x08\x00\xa0' + b'lH\x05Y\xfc8\x044\x00\x00\x00(\x04\x00\x00\x01' + b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + b'\x00aPK\x01\x02\x14\x00\x14\x00\x00\x00\x08\x00\xa0l' + b'H\x05\xe2\x1e8\xbb\x10\x00\x00\x00\t\x04\x00\x00\x01\x00' + b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00$\x00\x00\x00' + b'bPK\x05\x06\x00\x00\x00\x00\x02\x00\x02\x00^\x00\x00' + b'\x00S\x00\x00\x00\x00\x00' + ) + with zipfile.ZipFile(io.BytesIO(data), 'r') as zipf: + self.assertEqual(zipf.namelist(), ['a', 'b']) + zi = zipf.getinfo('a') + self.assertEqual(zi.header_offset, 0) + self.assertEqual(zi.compress_size, 52) + self.assertEqual(zi.file_size, 1064) + zi = zipf.getinfo('b') + self.assertEqual(zi.header_offset, 36) + self.assertEqual(zi.compress_size, 16) + self.assertEqual(zi.file_size, 1033) + with self.assertRaisesRegex(zipfile.BadZipFile, 'Overlapped entries'): + zipf.read('a') + self.assertEqual(len(zipf.read('b')), 1033) + + def tearDown(self): + unlink(TESTFN) + unlink(TESTFN2) + + +class AbstractBadCrcTests: + def test_testzip_with_bad_crc(self): + """Tests that files with bad CRCs return their name from testzip.""" + zipdata = self.zip_with_bad_crc + + with zipfile.ZipFile(io.BytesIO(zipdata), mode="r") as zipf: + # testzip returns the name of the first corrupt file, or None + self.assertEqual('afile', zipf.testzip()) + + def test_read_with_bad_crc(self): + """Tests that files with bad CRCs raise a BadZipFile exception when read.""" + zipdata = self.zip_with_bad_crc + + # Using ZipFile.read() + with zipfile.ZipFile(io.BytesIO(zipdata), mode="r") as zipf: + self.assertRaises(zipfile.BadZipFile, zipf.read, 'afile') + + # Using ZipExtFile.read() + with zipfile.ZipFile(io.BytesIO(zipdata), mode="r") as zipf: + with zipf.open('afile', 'r') as corrupt_file: + self.assertRaises(zipfile.BadZipFile, corrupt_file.read) + + # Same with small reads (in order to exercise the buffering logic) + with zipfile.ZipFile(io.BytesIO(zipdata), mode="r") as zipf: + with zipf.open('afile', 'r') as corrupt_file: + corrupt_file.MIN_READ_SIZE = 2 + with self.assertRaises(zipfile.BadZipFile): + while corrupt_file.read(2): + pass + + +class StoredBadCrcTests(AbstractBadCrcTests, unittest.TestCase): + compression = zipfile.ZIP_STORED + zip_with_bad_crc = ( + b'PK\003\004\024\0\0\0\0\0 \213\212;:r' + b'\253\377\f\0\0\0\f\0\0\0\005\0\0\000af' + b'ilehello,AworldP' + b'K\001\002\024\003\024\0\0\0\0\0 \213\212;:' + b'r\253\377\f\0\0\0\f\0\0\0\005\0\0\0\0' + b'\0\0\0\0\0\0\0\200\001\0\0\0\000afi' + b'lePK\005\006\0\0\0\0\001\0\001\0003\000' + b'\0\0/\0\0\0\0\0') + +@requires_zlib() +class DeflateBadCrcTests(AbstractBadCrcTests, unittest.TestCase): + compression = zipfile.ZIP_DEFLATED + zip_with_bad_crc = ( + b'PK\x03\x04\x14\x00\x00\x00\x08\x00n}\x0c=FA' + b'KE\x10\x00\x00\x00n\x00\x00\x00\x05\x00\x00\x00af' + b'ile\xcbH\xcd\xc9\xc9W(\xcf/\xcaI\xc9\xa0' + b'=\x13\x00PK\x01\x02\x14\x03\x14\x00\x00\x00\x08\x00n' + b'}\x0c=FAKE\x10\x00\x00\x00n\x00\x00\x00\x05' + b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x01\x00\x00\x00' + b'\x00afilePK\x05\x06\x00\x00\x00\x00\x01\x00' + b'\x01\x003\x00\x00\x003\x00\x00\x00\x00\x00') + +@requires_bz2() +class Bzip2BadCrcTests(AbstractBadCrcTests, unittest.TestCase): + compression = zipfile.ZIP_BZIP2 + zip_with_bad_crc = ( + b'PK\x03\x04\x14\x03\x00\x00\x0c\x00nu\x0c=FA' + b'KE8\x00\x00\x00n\x00\x00\x00\x05\x00\x00\x00af' + b'ileBZh91AY&SY\xd4\xa8\xca' + b'\x7f\x00\x00\x0f\x11\x80@\x00\x06D\x90\x80 \x00 \xa5' + b'P\xd9!\x03\x03\x13\x13\x13\x89\xa9\xa9\xc2u5:\x9f' + b'\x8b\xb9"\x9c(HjTe?\x80PK\x01\x02\x14' + b'\x03\x14\x03\x00\x00\x0c\x00nu\x0c=FAKE8' + b'\x00\x00\x00n\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x00' + b'\x00 \x80\x80\x81\x00\x00\x00\x00afilePK' + b'\x05\x06\x00\x00\x00\x00\x01\x00\x01\x003\x00\x00\x00[\x00' + b'\x00\x00\x00\x00') + +@requires_lzma() +class LzmaBadCrcTests(AbstractBadCrcTests, unittest.TestCase): + compression = zipfile.ZIP_LZMA + zip_with_bad_crc = ( + b'PK\x03\x04\x14\x03\x00\x00\x0e\x00nu\x0c=FA' + b'KE\x1b\x00\x00\x00n\x00\x00\x00\x05\x00\x00\x00af' + b'ile\t\x04\x05\x00]\x00\x00\x00\x04\x004\x19I' + b'\xee\x8d\xe9\x17\x89:3`\tq!.8\x00PK' + b'\x01\x02\x14\x03\x14\x03\x00\x00\x0e\x00nu\x0c=FA' + b'KE\x1b\x00\x00\x00n\x00\x00\x00\x05\x00\x00\x00\x00\x00' + b'\x00\x00\x00\x00 \x80\x80\x81\x00\x00\x00\x00afil' + b'ePK\x05\x06\x00\x00\x00\x00\x01\x00\x01\x003\x00\x00' + b'\x00>\x00\x00\x00\x00\x00') + + +class DecryptionTests(unittest.TestCase): + """Check that ZIP decryption works. Since the library does not + support encryption at the moment, we use a pre-generated encrypted + ZIP file.""" + + data = ( + b'PK\x03\x04\x14\x00\x01\x00\x00\x00n\x92i.#y\xef?&\x00\x00\x00\x1a\x00' + b'\x00\x00\x08\x00\x00\x00test.txt\xfa\x10\xa0gly|\xfa-\xc5\xc0=\xf9y' + b'\x18\xe0\xa8r\xb3Z}Lg\xbc\xae\xf9|\x9b\x19\xe4\x8b\xba\xbb)\x8c\xb0\xdbl' + b'PK\x01\x02\x14\x00\x14\x00\x01\x00\x00\x00n\x92i.#y\xef?&\x00\x00\x00' + b'\x1a\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00\x00\x01\x00 \x00\xb6\x81' + b'\x00\x00\x00\x00test.txtPK\x05\x06\x00\x00\x00\x00\x01\x00\x01\x006\x00' + b'\x00\x00L\x00\x00\x00\x00\x00' ) + data2 = ( + b'PK\x03\x04\x14\x00\t\x00\x08\x00\xcf}38xu\xaa\xb2\x14\x00\x00\x00\x00\x02' + b'\x00\x00\x04\x00\x15\x00zeroUT\t\x00\x03\xd6\x8b\x92G\xda\x8b\x92GUx\x04' + b'\x00\xe8\x03\xe8\x03\xc7", "exec") +raise_src = 'def do_raise(): raise TypeError\n' + +def make_pyc(co, mtime, size): + data = marshal.dumps(co) + pyc = (importlib.util.MAGIC_NUMBER + + struct.pack("", "exec"), NOW, len(src)) + files = {TESTMOD + pyc_ext: pyc, + "some.data": "some data"} + self.doTest(pyc_ext, files, TESTMOD, prefix='') + + def testDefaultOptimizationLevel(self): + # zipimport should use the default optimization level (#28131) + src = """if 1: # indent hack + def test(val): + assert(val) + return val\n""" + files = {TESTMOD + '.py': src} + self.makeZip(files) + sys.path.insert(0, TEMP_ZIP) + mod = importlib.import_module(TESTMOD) + self.assertEqual(mod.test(1), 1) + if __debug__: + self.assertRaises(AssertionError, mod.test, False) + else: + self.assertEqual(mod.test(0), 0) + + def testImport_WithStuff(self): + # try importing from a zipfile which contains additional + # stuff at the beginning of the file + files = {TESTMOD + ".py": test_src} + self.doTest(".py", files, TESTMOD, + stuff=b"Some Stuff"*31) + + def assertModuleSource(self, module): + self.assertEqual(inspect.getsource(module), test_src) + + def testGetSource(self): + files = {TESTMOD + ".py": test_src} + self.doTest(".py", files, TESTMOD, call=self.assertModuleSource) + + def testGetCompiledSource(self): + pyc = make_pyc(compile(test_src, "", "exec"), NOW, len(test_src)) + files = {TESTMOD + ".py": test_src, + TESTMOD + pyc_ext: pyc} + self.doTest(pyc_ext, files, TESTMOD, call=self.assertModuleSource) + + def runDoctest(self, callback): + files = {TESTMOD + ".py": test_src, + "xyz.txt": ">>> log.append(True)\n"} + self.doTest(".py", files, TESTMOD, call=callback) + + def doDoctestFile(self, module): + log = [] + old_master, doctest.master = doctest.master, None + try: + doctest.testfile( + 'xyz.txt', package=module, module_relative=True, + globs=locals() + ) + finally: + doctest.master = old_master + self.assertEqual(log,[True]) + + def testDoctestFile(self): + self.runDoctest(self.doDoctestFile) + + def doDoctestSuite(self, module): + log = [] + doctest.DocFileTest( + 'xyz.txt', package=module, module_relative=True, + globs=locals() + ).run() + self.assertEqual(log,[True]) + + def testDoctestSuite(self): + self.runDoctest(self.doDoctestSuite) + + def doTraceback(self, module): + try: + module.do_raise() + except Exception as e: + tb = e.__traceback__.tb_next + + f,lno,n,line = extract_tb(tb, 1)[0] + self.assertEqual(line, raise_src.strip()) + + f,lno,n,line = extract_stack(tb.tb_frame, 1)[0] + self.assertEqual(line, raise_src.strip()) + + s = io.StringIO() + print_tb(tb, 1, s) + self.assertTrue(s.getvalue().endswith( + ' def do_raise(): raise TypeError\n' + '' if support.has_no_debug_ranges() else + ' ^^^^^^^^^^^^^^^\n' + )) + else: + raise AssertionError("This ought to be impossible") + + def testTraceback(self): + files = {TESTMOD + ".py": raise_src} + self.doTest(None, files, TESTMOD, call=self.doTraceback) + + @unittest.skipIf(os_helper.TESTFN_UNENCODABLE is None, + "need an unencodable filename") + def testUnencodable(self): + filename = os_helper.TESTFN_UNENCODABLE + ".zip" + self.makeZip({TESTMOD + ".py": test_src}, filename) + spec = zipimport.zipimporter(filename).find_spec(TESTMOD) + mod = importlib.util.module_from_spec(spec) + spec.loader.exec_module(mod) + + def testBytesPath(self): + filename = os_helper.TESTFN + ".zip" + self.makeZip({TESTMOD + ".py": test_src}, filename) + + zipimport.zipimporter(filename) + with self.assertRaises(TypeError): + zipimport.zipimporter(os.fsencode(filename)) + with self.assertRaises(TypeError): + zipimport.zipimporter(bytearray(os.fsencode(filename))) + with self.assertRaises(TypeError): + zipimport.zipimporter(memoryview(os.fsencode(filename))) + + def testComment(self): + files = {TESTMOD + ".py": test_src} + self.doTest(".py", files, TESTMOD, comment=b"comment") + + def testBeginningCruftAndComment(self): + files = {TESTMOD + ".py": test_src} + self.doTest(".py", files, TESTMOD, stuff=b"cruft" * 64, comment=b"hi") + + def testLargestPossibleComment(self): + files = {TESTMOD + ".py": test_src} + self.doTest(".py", files, TESTMOD, comment=b"c" * ((1 << 16) - 1)) + + +@support.requires_zlib() +class CompressedZipImportTestCase(UncompressedZipImportTestCase): + compression = ZIP_DEFLATED + + +class BadFileZipImportTestCase(unittest.TestCase): + def assertZipFailure(self, filename): + self.assertRaises(zipimport.ZipImportError, + zipimport.zipimporter, filename) + + def testNoFile(self): + self.assertZipFailure('AdfjdkFJKDFJjdklfjs') + + def testEmptyFilename(self): + self.assertZipFailure('') + + def testBadArgs(self): + self.assertRaises(TypeError, zipimport.zipimporter, None) + self.assertRaises(TypeError, zipimport.zipimporter, TESTMOD, kwd=None) + self.assertRaises(TypeError, zipimport.zipimporter, + list(os.fsencode(TESTMOD))) + + def testFilenameTooLong(self): + self.assertZipFailure('A' * 33000) + + def testEmptyFile(self): + os_helper.unlink(TESTMOD) + os_helper.create_empty_file(TESTMOD) + self.assertZipFailure(TESTMOD) + + @unittest.skipIf(support.is_wasi, "mode 000 not supported.") + def testFileUnreadable(self): + os_helper.unlink(TESTMOD) + fd = os.open(TESTMOD, os.O_CREAT, 000) + try: + os.close(fd) + + with self.assertRaises(zipimport.ZipImportError) as cm: + zipimport.zipimporter(TESTMOD) + finally: + # If we leave "the read-only bit" set on Windows, nothing can + # delete TESTMOD, and later tests suffer bogus failures. + os.chmod(TESTMOD, 0o666) + os_helper.unlink(TESTMOD) + + def testNotZipFile(self): + os_helper.unlink(TESTMOD) + fp = open(TESTMOD, 'w+') + fp.write('a' * 22) + fp.close() + self.assertZipFailure(TESTMOD) + + # XXX: disabled until this works on Big-endian machines + def _testBogusZipFile(self): + os_helper.unlink(TESTMOD) + fp = open(TESTMOD, 'w+') + fp.write(struct.pack('=I', 0x06054B50)) + fp.write('a' * 18) + fp.close() + z = zipimport.zipimporter(TESTMOD) + + try: + with warnings.catch_warnings(): + warnings.simplefilter("ignore", DeprecationWarning) + self.assertRaises(TypeError, z.load_module, None) + self.assertRaises(TypeError, z.find_module, None) + self.assertRaises(TypeError, z.find_spec, None) + self.assertRaises(TypeError, z.exec_module, None) + self.assertRaises(TypeError, z.is_package, None) + self.assertRaises(TypeError, z.get_code, None) + self.assertRaises(TypeError, z.get_data, None) + self.assertRaises(TypeError, z.get_source, None) + + error = zipimport.ZipImportError + self.assertIsNone(z.find_spec('abc')) + + with warnings.catch_warnings(): + warnings.simplefilter("ignore", DeprecationWarning) + self.assertRaises(error, z.load_module, 'abc') + self.assertRaises(error, z.get_code, 'abc') + self.assertRaises(OSError, z.get_data, 'abc') + self.assertRaises(error, z.get_source, 'abc') + self.assertRaises(error, z.is_package, 'abc') + finally: + zipimport._zip_directory_cache.clear() + + +def tearDownModule(): + os_helper.unlink(TESTMOD) + + +if __name__ == "__main__": + unittest.main() diff --git a/cpython-tests/test_zipimport_support.py b/cpython-tests/test_zipimport_support.py new file mode 100644 index 0000000..71039d2 --- /dev/null +++ b/cpython-tests/test_zipimport_support.py @@ -0,0 +1,244 @@ +# This test module covers support in various parts of the standard library +# for working with modules located inside zipfiles +# The tests are centralised in this fashion to make it easy to drop them +# if a platform doesn't support zipimport +import test.support +import os +import os.path +import sys +import textwrap +import zipfile +import zipimport +import doctest +import inspect +import linecache +import unittest +from test.support import os_helper +from test.support.script_helper import (spawn_python, kill_python, assert_python_ok, + make_script, make_zip_script) + +verbose = test.support.verbose + +# Library modules covered by this test set +# pdb (Issue 4201) +# inspect (Issue 4223) +# doctest (Issue 4197) + +# Other test modules with zipimport related tests +# test_zipimport (of course!) +# test_cmd_line_script (covers the zipimport support in runpy) + +# Retrieve some helpers from other test cases +from test.test_doctest import (test_doctest, + sample_doctest, sample_doctest_no_doctests, + sample_doctest_no_docstrings) + + +def _run_object_doctest(obj, module): + finder = doctest.DocTestFinder(verbose=verbose, recurse=False) + runner = doctest.DocTestRunner(verbose=verbose) + # Use the object's fully qualified name if it has one + # Otherwise, use the module's name + try: + name = "%s.%s" % (obj.__module__, obj.__qualname__) + except AttributeError: + name = module.__name__ + for example in finder.find(obj, name, module): + runner.run(example) + f, t = runner.failures, runner.tries + if f: + raise test.support.TestFailed("%d of %d doctests failed" % (f, t)) + if verbose: + print ('doctest (%s) ... %d tests with zero failures' % (module.__name__, t)) + return f, t + + + +class ZipSupportTests(unittest.TestCase): + # This used to use the ImportHooksBaseTestCase to restore + # the state of the import related information + # in the sys module after each test. However, that restores + # *too much* information and breaks for the invocation + # of test_doctest. So we do our own thing and leave + # sys.modules alone. + # We also clear the linecache and zipimport cache + # just to avoid any bogus errors due to name reuse in the tests + def setUp(self): + linecache.clearcache() + zipimport._zip_directory_cache.clear() + self.path = sys.path[:] + self.meta_path = sys.meta_path[:] + self.path_hooks = sys.path_hooks[:] + sys.path_importer_cache.clear() + + def tearDown(self): + sys.path[:] = self.path + sys.meta_path[:] = self.meta_path + sys.path_hooks[:] = self.path_hooks + sys.path_importer_cache.clear() + + def test_inspect_getsource_issue4223(self): + test_src = "def foo(): pass\n" + with os_helper.temp_dir() as d: + init_name = make_script(d, '__init__', test_src) + name_in_zip = os.path.join('zip_pkg', + os.path.basename(init_name)) + zip_name, run_name = make_zip_script(d, 'test_zip', + init_name, name_in_zip) + os.remove(init_name) + sys.path.insert(0, zip_name) + import zip_pkg + try: + self.assertEqual(inspect.getsource(zip_pkg.foo), test_src) + finally: + del sys.modules["zip_pkg"] + + def test_doctest_issue4197(self): + # To avoid having to keep two copies of the doctest module's + # unit tests in sync, this test works by taking the source of + # test_doctest itself, rewriting it a bit to cope with a new + # location, and then throwing it in a zip file to make sure + # everything still works correctly + test_src = inspect.getsource(test_doctest) + test_src = test_src.replace( + "from test.test_doctest import test_doctest", + "import test_zipped_doctest as test_doctest") + test_src = test_src.replace("test.test_doctest.test_doctest", + "test_zipped_doctest") + test_src = test_src.replace("test.test_doctest.sample_doctest", + "sample_zipped_doctest") + # The sample doctest files rewritten to include in the zipped version. + sample_sources = {} + for mod in [sample_doctest, sample_doctest_no_doctests, + sample_doctest_no_docstrings]: + src = inspect.getsource(mod) + src = src.replace("test.test_doctest.test_doctest", "test_zipped_doctest") + # Rewrite the module name so that, for example, + # "test.sample_doctest" becomes "sample_zipped_doctest". + mod_name = mod.__name__.split(".")[-1] + mod_name = mod_name.replace("sample_", "sample_zipped_") + sample_sources[mod_name] = src + + with os_helper.temp_dir() as d: + script_name = make_script(d, 'test_zipped_doctest', + test_src) + zip_name, run_name = make_zip_script(d, 'test_zip', + script_name) + with zipfile.ZipFile(zip_name, 'a') as z: + for mod_name, src in sample_sources.items(): + z.writestr(mod_name + ".py", src) + if verbose: + with zipfile.ZipFile(zip_name, 'r') as zip_file: + print ('Contents of %r:' % zip_name) + zip_file.printdir() + os.remove(script_name) + sys.path.insert(0, zip_name) + import test_zipped_doctest + try: + # Some of the doc tests depend on the colocated text files + # which aren't available to the zipped version (the doctest + # module currently requires real filenames for non-embedded + # tests). So we're forced to be selective about which tests + # to run. + # doctest could really use some APIs which take a text + # string or a file object instead of a filename... + known_good_tests = [ + test_zipped_doctest.SampleClass, + test_zipped_doctest.SampleClass.NestedClass, + test_zipped_doctest.SampleClass.NestedClass.__init__, + test_zipped_doctest.SampleClass.__init__, + test_zipped_doctest.SampleClass.a_classmethod, + test_zipped_doctest.SampleClass.a_property, + test_zipped_doctest.SampleClass.a_staticmethod, + test_zipped_doctest.SampleClass.double, + test_zipped_doctest.SampleClass.get, + test_zipped_doctest.SampleNewStyleClass, + test_zipped_doctest.SampleNewStyleClass.__init__, + test_zipped_doctest.SampleNewStyleClass.double, + test_zipped_doctest.SampleNewStyleClass.get, + test_zipped_doctest.sample_func, + test_zipped_doctest.test_DocTest, + test_zipped_doctest.test_DocTestParser, + test_zipped_doctest.test_DocTestRunner.basics, + test_zipped_doctest.test_DocTestRunner.exceptions, + test_zipped_doctest.test_DocTestRunner.option_directives, + test_zipped_doctest.test_DocTestRunner.optionflags, + test_zipped_doctest.test_DocTestRunner.verbose_flag, + test_zipped_doctest.test_Example, + test_zipped_doctest.test_debug, + test_zipped_doctest.test_testsource, + test_zipped_doctest.test_trailing_space_in_test, + test_zipped_doctest.test_DocTestSuite, + test_zipped_doctest.test_DocTestFinder, + ] + # These tests are the ones which need access + # to the data files, so we don't run them + fail_due_to_missing_data_files = [ + test_zipped_doctest.test_DocFileSuite, + test_zipped_doctest.test_testfile, + test_zipped_doctest.test_unittest_reportflags, + ] + + for obj in known_good_tests: + _run_object_doctest(obj, test_zipped_doctest) + finally: + del sys.modules["test_zipped_doctest"] + + def test_doctest_main_issue4197(self): + test_src = textwrap.dedent("""\ + class Test: + ">>> 'line 2'" + pass + + import doctest + doctest.testmod() + """) + pattern = 'File "%s", line 2, in %s' + with os_helper.temp_dir() as d: + script_name = make_script(d, 'script', test_src) + rc, out, err = assert_python_ok(script_name) + expected = pattern % (script_name, "__main__.Test") + if verbose: + print ("Expected line", expected) + print ("Got stdout:") + print (ascii(out)) + self.assertIn(expected.encode('utf-8'), out) + zip_name, run_name = make_zip_script(d, "test_zip", + script_name, '__main__.py') + rc, out, err = assert_python_ok(zip_name) + expected = pattern % (run_name, "__main__.Test") + if verbose: + print ("Expected line", expected) + print ("Got stdout:") + print (ascii(out)) + self.assertIn(expected.encode('utf-8'), out) + + def test_pdb_issue4201(self): + test_src = textwrap.dedent("""\ + def f(): + pass + + import pdb + pdb.Pdb(nosigint=True).runcall(f) + """) + with os_helper.temp_dir() as d: + script_name = make_script(d, 'script', test_src) + p = spawn_python(script_name) + p.stdin.write(b'l\n') + data = kill_python(p) + # bdb/pdb applies normcase to its filename before displaying + self.assertIn(os.path.normcase(script_name.encode('utf-8')), data) + zip_name, run_name = make_zip_script(d, "test_zip", + script_name, '__main__.py') + p = spawn_python(zip_name) + p.stdin.write(b'l\n') + data = kill_python(p) + # bdb/pdb applies normcase to its filename before displaying + self.assertIn(os.path.normcase(run_name.encode('utf-8')), data) + + +def tearDownModule(): + test.support.reap_children() + +if __name__ == '__main__': + unittest.main() diff --git a/cpython-tests/zip_cp437_header.zip b/cpython-tests/zip_cp437_header.zip new file mode 100644 index 0000000000000000000000000000000000000000..f7c6cf170422c48ce1337a4c05843fe66a4b0bbf GIT binary patch literal 270 zcmWIWW@Zs#U}E54IGj1(Hjhth3p)b?11k`V0&!YqPHJ9aZfbmaW=Tf;#QZkBl8Tbz zujP-QGBE_;)G30lGrzP1MI$4V2m@|Qfx3Y}0Zig>LV!1JBS5x8fC7-R0%8=FkcTh5&DbW=z*2 uYc>UI2Du%m9b!Lz?W_zi?FI&&mdJ6*Ca-0n$rA90maKRyZ91 literal 0 HcmV?d00001 diff --git a/cpython-tests/zipdir_backslash.zip b/cpython-tests/zipdir_backslash.zip new file mode 100644 index 0000000000000000000000000000000000000000..979126ef5e37ebd46762c76439e9b4e77431103c GIT binary patch literal 192 zcmWIWW@Zs#0Dexe_with_zip +rm zip2.zip +``` + +### Modern format (4.5) zip64 file + +Redirecting from stdin forces Info-ZIP's zip tool to create a zip64. + +``` +zip -0 zip64.zip +cat header.sh zip64.zip >exe_with_z64 +rm zip64.zip +``` + diff --git a/cpython-tests/ziptestdata/exe_with_z64 b/cpython-tests/ziptestdata/exe_with_z64 new file mode 100644 index 0000000000000000000000000000000000000000..82b03cf39d919d9de05866b4f7cdf1dfe47e6eb3 GIT binary patch literal 978 zcmaJ<-EY${5KkEl$xl4ME4rnV(xNp330}q~w6RoFg|_N41e7XrQ)jhEoXByzLbV6} z7bN~nxXZ`b5aOcPclPh+`)uy)&!pO)@qEF01K%5u#vZQ0`QQ{+-#hbQJlMzfN zumhbn*t?s5Bd=_jPG5pq2*m(Jgo_mHo-#sbTHp%FGB+?21c5M360YVDOC^Boi)A8| zaqW`1mIj`)NHXt(_xjvFK6&e598YZ!YZ3l8f{q6rI6U+Qr@^orj6V8rh65&(EY$|m zyw<+SERwNcOz}kI84m>G zSHN@NP(AKCZFVWm;@bWsvo1d0s^NQ(q;qlPXs1m?Of5j_0ahSNH4rM0DoR1B`pzXg zmbq!Q2?j9dhGVD|)zyN}i{}esyQ-xKTZG$#>tt`JC1{4sF91y#s`x7`^Rh*e)YvZy zgkqqkaUCw?O1P{lg45-zR7)d3Et45`xQsPi8a|7~fpf#r#O@xyAC7yz7Yxqdop@t= z+GecTY{GH*C{6^9iZVG|$~dMm;TcwVF6O`^njW)|c@d2ZNMpBKJnC=V?N}s_K0g`u zf>&q1Drr~`txm&wV0p#0b-g#i7nomB!y-wOlGog%8hujhFq@*CrC0V>0$BJLY}9Yu zdAxPoGdZHaQ8}dT$9GygqyF~x9%(2wjr1B?@B4I!vMx6ZdG|^ES=ode_3v$y*}#wR GCH6PMMGy`E literal 0 HcmV?d00001 diff --git a/cpython-tests/ziptestdata/exe_with_zip b/cpython-tests/ziptestdata/exe_with_zip new file mode 100644 index 0000000000000000000000000000000000000000..c833cdf9f934b08fc449da077efae44aa9a06f18 GIT binary patch literal 990 zcmah{&2G~`5Ozt6#BxUB0BGt|a)MeHBzh^WP)i*V6_O}*5D+43W3QVP_S)KAHz`pi zcm>WJc^aOC$6(e@N~!o+%den-3;)Sr%Y!Z0+w(d{LAMq3-uf@P z9m3N*lNvI$JbmPO%o9e4pea*14H@ji{DR<>2{Yf&&6LZ;8JC$DI=^T*Ba%xlbR%}U zITKu*!h8w30IGn(BDw1{$&~BKrT>oSEll57hHpZeMQq=ZPSXIfv;d*Is6d=aFi`;) zaRyv0|GCCbxYCWL2?L0zrbu-GbtR)wnZ5)z7h1BgVd6I7ve+xfDrk(z4*+%OisT#$ zRkbMQ68mL{7!IasRE86N#$2)x!D-R6OmfXY6zLc{TyYHxO~(n_b*@}Av|9(SyZyHB z1)agGL$7a-nuOHrbvUS!;zZ!62(4hslf;Y(%~9cqML=USJ$k}b$;JhQk>6X~JFcw~ z%d9)^A9mZpvl9=`=Dly-vourMXb_;{MX9UeQ7N~ZpAY<7R_*(II{NZyIx1$jt(Dau zHOneZ9ejjVI+sG|%rH|rlgP`o7b`AXUNIxrip1vZklyjijR&>AvAb(Xm+RYSv;Bwb WTE+Dm&))IcO#@!RC&c}$ajc&zD=F;& literal 0 HcmV?d00001 diff --git a/cpython-tests/ziptestdata/header.sh b/cpython-tests/ziptestdata/header.sh new file mode 100644 index 0000000..52dc91a --- /dev/null +++ b/cpython-tests/ziptestdata/header.sh @@ -0,0 +1,24 @@ +#!/bin/bash +INTERPRETER_UNDER_TEST="$1" +if [[ ! -x "${INTERPRETER_UNDER_TEST}" ]]; then + echo "Interpreter must be the command line argument." + exit 4 +fi +EXECUTABLE="$0" exec "${INTERPRETER_UNDER_TEST}" -E - < Date: Sat, 28 Sep 2024 14:30:47 +0100 Subject: [PATCH 02/41] Remvoe tests for PyZipFile --- cpython-tests/test_zipfile/test_core.py | 193 ------------------------ 1 file changed, 193 deletions(-) diff --git a/cpython-tests/test_zipfile/test_core.py b/cpython-tests/test_zipfile/test_core.py index 5b32f80..1930923 100644 --- a/cpython-tests/test_zipfile/test_core.py +++ b/cpython-tests/test_zipfile/test_core.py @@ -1335,199 +1335,6 @@ class LzmaWriterTests(AbstractWriterTests, unittest.TestCase): compression = zipfile.ZIP_LZMA -class PyZipFileTests(unittest.TestCase): - def assertCompiledIn(self, name, namelist): - if name + 'o' not in namelist: - self.assertIn(name + 'c', namelist) - - def requiresWriteAccess(self, path): - # effective_ids unavailable on windows - if not os.access(path, os.W_OK, - effective_ids=os.access in os.supports_effective_ids): - self.skipTest('requires write access to the installed location') - filename = os.path.join(path, 'test_zipfile.try') - try: - fd = os.open(filename, os.O_WRONLY | os.O_CREAT) - os.close(fd) - except Exception: - self.skipTest('requires write access to the installed location') - unlink(filename) - - def test_write_pyfile(self): - self.requiresWriteAccess(os.path.dirname(__file__)) - with TemporaryFile() as t, zipfile.PyZipFile(t, "w") as zipfp: - fn = __file__ - if fn.endswith('.pyc'): - path_split = fn.split(os.sep) - if os.altsep is not None: - path_split.extend(fn.split(os.altsep)) - if '__pycache__' in path_split: - fn = importlib.util.source_from_cache(fn) - else: - fn = fn[:-1] - - zipfp.writepy(fn) - - bn = os.path.basename(fn) - self.assertNotIn(bn, zipfp.namelist()) - self.assertCompiledIn(bn, zipfp.namelist()) - - with TemporaryFile() as t, zipfile.PyZipFile(t, "w") as zipfp: - fn = __file__ - if fn.endswith('.pyc'): - fn = fn[:-1] - - zipfp.writepy(fn, "testpackage") - - bn = "%s/%s" % ("testpackage", os.path.basename(fn)) - self.assertNotIn(bn, zipfp.namelist()) - self.assertCompiledIn(bn, zipfp.namelist()) - - def test_write_python_package(self): - import email - packagedir = os.path.dirname(email.__file__) - self.requiresWriteAccess(packagedir) - - with TemporaryFile() as t, zipfile.PyZipFile(t, "w") as zipfp: - zipfp.writepy(packagedir) - - # Check for a couple of modules at different levels of the - # hierarchy - names = zipfp.namelist() - self.assertCompiledIn('email/__init__.py', names) - self.assertCompiledIn('email/mime/text.py', names) - - def test_write_filtered_python_package(self): - import test - packagedir = os.path.dirname(test.__file__) - self.requiresWriteAccess(packagedir) - - with TemporaryFile() as t, zipfile.PyZipFile(t, "w") as zipfp: - - # first make sure that the test folder gives error messages - # (on the badsyntax_... files) - with captured_stdout() as reportSIO: - zipfp.writepy(packagedir) - reportStr = reportSIO.getvalue() - self.assertTrue('SyntaxError' in reportStr) - - # then check that the filter works on the whole package - with captured_stdout() as reportSIO: - zipfp.writepy(packagedir, filterfunc=lambda whatever: False) - reportStr = reportSIO.getvalue() - self.assertTrue('SyntaxError' not in reportStr) - - # then check that the filter works on individual files - def filter(path): - return not os.path.basename(path).startswith("bad") - with captured_stdout() as reportSIO, self.assertWarns(UserWarning): - zipfp.writepy(packagedir, filterfunc=filter) - reportStr = reportSIO.getvalue() - if reportStr: - print(reportStr) - self.assertTrue('SyntaxError' not in reportStr) - - def test_write_with_optimization(self): - import email - packagedir = os.path.dirname(email.__file__) - self.requiresWriteAccess(packagedir) - optlevel = 1 if __debug__ else 0 - ext = '.pyc' - - with TemporaryFile() as t, \ - zipfile.PyZipFile(t, "w", optimize=optlevel) as zipfp: - zipfp.writepy(packagedir) - - names = zipfp.namelist() - self.assertIn('email/__init__' + ext, names) - self.assertIn('email/mime/text' + ext, names) - - def test_write_python_directory(self): - os.mkdir(TESTFN2) - try: - with open(os.path.join(TESTFN2, "mod1.py"), "w", encoding='utf-8') as fp: - fp.write("print(42)\n") - - with open(os.path.join(TESTFN2, "mod2.py"), "w", encoding='utf-8') as fp: - fp.write("print(42 * 42)\n") - - with open(os.path.join(TESTFN2, "mod2.txt"), "w", encoding='utf-8') as fp: - fp.write("bla bla bla\n") - - with TemporaryFile() as t, zipfile.PyZipFile(t, "w") as zipfp: - zipfp.writepy(TESTFN2) - - names = zipfp.namelist() - self.assertCompiledIn('mod1.py', names) - self.assertCompiledIn('mod2.py', names) - self.assertNotIn('mod2.txt', names) - - finally: - rmtree(TESTFN2) - - def test_write_python_directory_filtered(self): - os.mkdir(TESTFN2) - try: - with open(os.path.join(TESTFN2, "mod1.py"), "w", encoding='utf-8') as fp: - fp.write("print(42)\n") - - with open(os.path.join(TESTFN2, "mod2.py"), "w", encoding='utf-8') as fp: - fp.write("print(42 * 42)\n") - - with TemporaryFile() as t, zipfile.PyZipFile(t, "w") as zipfp: - zipfp.writepy(TESTFN2, filterfunc=lambda fn: - not fn.endswith('mod2.py')) - - names = zipfp.namelist() - self.assertCompiledIn('mod1.py', names) - self.assertNotIn('mod2.py', names) - - finally: - rmtree(TESTFN2) - - def test_write_non_pyfile(self): - with TemporaryFile() as t, zipfile.PyZipFile(t, "w") as zipfp: - with open(TESTFN, 'w', encoding='utf-8') as f: - f.write('most definitely not a python file') - self.assertRaises(RuntimeError, zipfp.writepy, TESTFN) - unlink(TESTFN) - - def test_write_pyfile_bad_syntax(self): - os.mkdir(TESTFN2) - try: - with open(os.path.join(TESTFN2, "mod1.py"), "w", encoding='utf-8') as fp: - fp.write("Bad syntax in python file\n") - - with TemporaryFile() as t, zipfile.PyZipFile(t, "w") as zipfp: - # syntax errors are printed to stdout - with captured_stdout() as s: - zipfp.writepy(os.path.join(TESTFN2, "mod1.py")) - - self.assertIn("SyntaxError", s.getvalue()) - - # as it will not have compiled the python file, it will - # include the .py file not .pyc - names = zipfp.namelist() - self.assertIn('mod1.py', names) - self.assertNotIn('mod1.pyc', names) - - finally: - rmtree(TESTFN2) - - def test_write_pathlike(self): - os.mkdir(TESTFN2) - try: - with open(os.path.join(TESTFN2, "mod1.py"), "w", encoding='utf-8') as fp: - fp.write("print(42)\n") - - with TemporaryFile() as t, zipfile.PyZipFile(t, "w") as zipfp: - zipfp.writepy(FakePath(os.path.join(TESTFN2, "mod1.py"))) - names = zipfp.namelist() - self.assertCompiledIn('mod1.py', names) - finally: - rmtree(TESTFN2) - - class ExtractTests(unittest.TestCase): def make_test_file(self): From b66ddbb8b5344b7ebf2c6a2188aa7023aaefe854 Mon Sep 17 00:00:00 2001 From: Ioannis Tziakos Date: Sat, 28 Sep 2024 14:31:23 +0100 Subject: [PATCH 03/41] Adapt test_zipfile.test_core tests to check against zipfile2 --- cpython-tests/test_zipfile/test_core.py | 472 ++++++++++++------------ 1 file changed, 237 insertions(+), 235 deletions(-) diff --git a/cpython-tests/test_zipfile/test_core.py b/cpython-tests/test_zipfile/test_core.py index 1930923..56da079 100644 --- a/cpython-tests/test_zipfile/test_core.py +++ b/cpython-tests/test_zipfile/test_core.py @@ -17,6 +17,8 @@ from tempfile import TemporaryFile from random import randint, random, randbytes +import zipfile2 + from test import archiver_tests from test.support import script_helper from test.support import ( @@ -63,7 +65,7 @@ def setUp(self): def make_test_archive(self, f, compression, compresslevel=None): kwargs = {'compression': compression, 'compresslevel': compresslevel} # Create the ZIP archive - with zipfile.ZipFile(f, "w", **kwargs) as zipfp: + with zipfile2.ZipFile(f, "w", **kwargs) as zipfp: zipfp.write(TESTFN, "another.name") zipfp.write(TESTFN, TESTFN) zipfp.writestr("strfile", self.data) @@ -75,7 +77,7 @@ def zip_test(self, f, compression, compresslevel=None): self.make_test_archive(f, compression, compresslevel) # Read the ZIP archive - with zipfile.ZipFile(f, "r", compression) as zipfp: + with zipfile2.ZipFile(f, "r", compression) as zipfp: self.assertEqual(zipfp.read(TESTFN), self.data) self.assertEqual(zipfp.read("another.name"), self.data) self.assertEqual(zipfp.read("strfile"), self.data) @@ -134,7 +136,7 @@ def zip_open_test(self, f, compression): self.make_test_archive(f, compression) # Read the ZIP archive - with zipfile.ZipFile(f, "r", compression) as zipfp: + with zipfile2.ZipFile(f, "r", compression) as zipfp: zipdata1 = [] with zipfp.open(TESTFN) as zipopen1: while True: @@ -161,14 +163,14 @@ def test_open(self): def test_open_with_pathlike(self): path = FakePath(TESTFN2) self.zip_open_test(path, self.compression) - with zipfile.ZipFile(path, "r", self.compression) as zipfp: + with zipfile2.ZipFile(path, "r", self.compression) as zipfp: self.assertIsInstance(zipfp.filename, str) def zip_random_open_test(self, f, compression): self.make_test_archive(f, compression) # Read the ZIP archive - with zipfile.ZipFile(f, "r", compression) as zipfp: + with zipfile2.ZipFile(f, "r", compression) as zipfp: zipdata1 = [] with zipfp.open(TESTFN) as zipopen1: while True: @@ -187,7 +189,7 @@ def zip_read1_test(self, f, compression): self.make_test_archive(f, compression) # Read the ZIP archive - with zipfile.ZipFile(f, "r") as zipfp, \ + with zipfile2.ZipFile(f, "r") as zipfp, \ zipfp.open(TESTFN) as zipopen: zipdata = [] while True: @@ -206,7 +208,7 @@ def zip_read1_10_test(self, f, compression): self.make_test_archive(f, compression) # Read the ZIP archive - with zipfile.ZipFile(f, "r") as zipfp, \ + with zipfile2.ZipFile(f, "r") as zipfp, \ zipfp.open(TESTFN) as zipopen: zipdata = [] while True: @@ -226,7 +228,7 @@ def zip_readline_read_test(self, f, compression): self.make_test_archive(f, compression) # Read the ZIP archive - with zipfile.ZipFile(f, "r") as zipfp, \ + with zipfile2.ZipFile(f, "r") as zipfp, \ zipfp.open(TESTFN) as zipopen: data = b'' while True: @@ -251,7 +253,7 @@ def zip_readline_test(self, f, compression): self.make_test_archive(f, compression) # Read the ZIP archive - with zipfile.ZipFile(f, "r") as zipfp: + with zipfile2.ZipFile(f, "r") as zipfp: with zipfp.open(TESTFN) as zipopen: for line in self.line_gen: linedata = zipopen.readline() @@ -265,7 +267,7 @@ def zip_readlines_test(self, f, compression): self.make_test_archive(f, compression) # Read the ZIP archive - with zipfile.ZipFile(f, "r") as zipfp: + with zipfile2.ZipFile(f, "r") as zipfp: with zipfp.open(TESTFN) as zipopen: ziplines = zipopen.readlines() for line, zipline in zip(self.line_gen, ziplines): @@ -279,7 +281,7 @@ def zip_iterlines_test(self, f, compression): self.make_test_archive(f, compression) # Read the ZIP archive - with zipfile.ZipFile(f, "r") as zipfp: + with zipfile2.ZipFile(f, "r") as zipfp: with zipfp.open(TESTFN) as zipopen: for line, zipline in zip(self.line_gen, zipopen): self.assertEqual(zipline, line) @@ -291,23 +293,23 @@ def test_iterlines(self): def test_low_compression(self): """Check for cases where compressed data is larger than original.""" # Create the ZIP archive - with zipfile.ZipFile(TESTFN2, "w", self.compression) as zipfp: + with zipfile2.ZipFile(TESTFN2, "w", self.compression) as zipfp: zipfp.writestr("strfile", '12') # Get an open object for strfile - with zipfile.ZipFile(TESTFN2, "r", self.compression) as zipfp: + with zipfile2.ZipFile(TESTFN2, "r", self.compression) as zipfp: with zipfp.open("strfile") as openobj: self.assertEqual(openobj.read(1), b'1') self.assertEqual(openobj.read(1), b'2') def test_writestr_compression(self): - zipfp = zipfile.ZipFile(TESTFN2, "w") + zipfp = zipfile2.ZipFile(TESTFN2, "w") zipfp.writestr("b.txt", "hello world", compress_type=self.compression) info = zipfp.getinfo('b.txt') self.assertEqual(info.compress_type, self.compression) def test_writestr_compresslevel(self): - zipfp = zipfile.ZipFile(TESTFN2, "w", compresslevel=1) + zipfp = zipfile2.ZipFile(TESTFN2, "w", compresslevel=1) zipfp.writestr("a.txt", "hello world", compress_type=self.compression) zipfp.writestr("b.txt", "hello world", compress_type=self.compression, compresslevel=2) @@ -328,7 +330,7 @@ def test_read_return_size(self): for test_size in (1, 4095, 4096, 4097, 16384): file_size = test_size + 1 junk = randbytes(file_size) - with zipfile.ZipFile(io.BytesIO(), "w", self.compression) as zipf: + with zipfile2.ZipFile(io.BytesIO(), "w", self.compression) as zipf: zipf.writestr('foo', junk) with zipf.open('foo', 'r') as fp: buf = fp.read(test_size) @@ -336,20 +338,20 @@ def test_read_return_size(self): def test_truncated_zipfile(self): fp = io.BytesIO() - with zipfile.ZipFile(fp, mode='w') as zipf: + with zipfile2.ZipFile(fp, mode='w') as zipf: zipf.writestr('strfile', self.data, compress_type=self.compression) end_offset = fp.tell() zipfiledata = fp.getvalue() fp = io.BytesIO(zipfiledata) - with zipfile.ZipFile(fp) as zipf: + with zipfile2.ZipFile(fp) as zipf: with zipf.open('strfile') as zipopen: fp.truncate(end_offset - 20) with self.assertRaises(EOFError): zipopen.read() fp = io.BytesIO(zipfiledata) - with zipfile.ZipFile(fp) as zipf: + with zipfile2.ZipFile(fp) as zipf: with zipf.open('strfile') as zipopen: fp.truncate(end_offset - 20) with self.assertRaises(EOFError): @@ -357,7 +359,7 @@ def test_truncated_zipfile(self): pass fp = io.BytesIO(zipfiledata) - with zipfile.ZipFile(fp) as zipf: + with zipfile2.ZipFile(fp) as zipf: with zipf.open('strfile') as zipopen: fp.truncate(end_offset - 20) with self.assertRaises(EOFError): @@ -367,12 +369,12 @@ def test_truncated_zipfile(self): def test_repr(self): fname = 'file.name' for f in get_files(self): - with zipfile.ZipFile(f, 'w', self.compression) as zipfp: + with zipfile2.ZipFile(f, 'w', self.compression) as zipfp: zipfp.write(TESTFN, fname) r = repr(zipfp) self.assertIn("mode='w'", r) - with zipfile.ZipFile(f, 'r') as zipfp: + with zipfile2.ZipFile(f, 'r') as zipfp: r = repr(zipfp) if isinstance(f, str): self.assertIn('filename=%r' % f, r) @@ -402,7 +404,7 @@ def test_compresslevel_basic(self): def test_per_file_compresslevel(self): """Check that files within a Zip archive can have different compression levels.""" - with zipfile.ZipFile(TESTFN2, "w", compresslevel=1) as zipfp: + with zipfile2.ZipFile(TESTFN2, "w", compresslevel=1) as zipfp: zipfp.write(TESTFN, 'compress_1') zipfp.write(TESTFN, 'compress_9', compresslevel=9) one_info = zipfp.getinfo('compress_1') @@ -424,7 +426,7 @@ def write(self, data): while True: testfile = BrokenFile() count = None - with zipfile.ZipFile(testfile, 'w', self.compression) as zipfp: + with zipfile2.ZipFile(testfile, 'w', self.compression) as zipfp: with zipfp.open('file1', 'w') as f: f.write(b'data1') count = 0 @@ -437,21 +439,21 @@ def write(self, data): break finally: count = None - with zipfile.ZipFile(io.BytesIO(testfile.getvalue())) as zipfp: + with zipfile2.ZipFile(io.BytesIO(testfile.getvalue())) as zipfp: self.assertEqual(zipfp.namelist(), ['file1']) self.assertEqual(zipfp.read('file1'), b'data1') - with zipfile.ZipFile(io.BytesIO(testfile.getvalue())) as zipfp: + with zipfile2.ZipFile(io.BytesIO(testfile.getvalue())) as zipfp: self.assertEqual(zipfp.namelist(), ['file1', 'file2']) self.assertEqual(zipfp.read('file1'), b'data1') self.assertEqual(zipfp.read('file2'), b'data2') def test_zipextfile_attrs(self): fname = "somefile.txt" - with zipfile.ZipFile(TESTFN2, mode="w") as zipfp: + with zipfile2.ZipFile(TESTFN2, mode="w") as zipfp: zipfp.writestr(fname, "bogus") - with zipfile.ZipFile(TESTFN2, mode="r") as zipfp: + with zipfile2.ZipFile(TESTFN2, mode="r") as zipfp: with zipfp.open(fname) as fid: self.assertEqual(fid.name, fname) self.assertRaises(io.UnsupportedOperation, fid.fileno) @@ -484,7 +486,7 @@ def zip_test_writestr_permissions(self, f, compression): # instance. self.make_test_archive(f, compression) - with zipfile.ZipFile(f, "r") as zipfp: + with zipfile2.ZipFile(f, "r") as zipfp: zinfo = zipfp.getinfo('strfile') self.assertEqual(zinfo.external_attr, 0o600 << 16) @@ -496,18 +498,18 @@ def test_writestr_permissions(self): self.zip_test_writestr_permissions(f, zipfile.ZIP_STORED) def test_absolute_arcnames(self): - with zipfile.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED) as zipfp: + with zipfile2.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED) as zipfp: zipfp.write(TESTFN, "/absolute") - with zipfile.ZipFile(TESTFN2, "r", zipfile.ZIP_STORED) as zipfp: + with zipfile2.ZipFile(TESTFN2, "r", zipfile.ZIP_STORED) as zipfp: self.assertEqual(zipfp.namelist(), ["absolute"]) def test_append_to_zip_file(self): """Test appending to an existing zipfile.""" - with zipfile.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED) as zipfp: + with zipfile2.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED) as zipfp: zipfp.write(TESTFN, TESTFN) - with zipfile.ZipFile(TESTFN2, "a", zipfile.ZIP_STORED) as zipfp: + with zipfile2.ZipFile(TESTFN2, "a", zipfile.ZIP_STORED) as zipfp: zipfp.writestr("strfile", self.data) self.assertEqual(zipfp.namelist(), [TESTFN, "strfile"]) @@ -519,24 +521,24 @@ def test_append_to_non_zip_file(self): with open(TESTFN2, 'wb') as f: f.write(data) - with zipfile.ZipFile(TESTFN2, "a", zipfile.ZIP_STORED) as zipfp: + with zipfile2.ZipFile(TESTFN2, "a", zipfile.ZIP_STORED) as zipfp: zipfp.write(TESTFN, TESTFN) with open(TESTFN2, 'rb') as f: f.seek(len(data)) - with zipfile.ZipFile(f, "r") as zipfp: + with zipfile2.ZipFile(f, "r") as zipfp: self.assertEqual(zipfp.namelist(), [TESTFN]) self.assertEqual(zipfp.read(TESTFN), self.data) with open(TESTFN2, 'rb') as f: self.assertEqual(f.read(len(data)), data) zipfiledata = f.read() - with io.BytesIO(zipfiledata) as bio, zipfile.ZipFile(bio) as zipfp: + with io.BytesIO(zipfiledata) as bio, zipfile2.ZipFile(bio) as zipfp: self.assertEqual(zipfp.namelist(), [TESTFN]) self.assertEqual(zipfp.read(TESTFN), self.data) def test_read_concatenated_zip_file(self): with io.BytesIO() as bio: - with zipfile.ZipFile(bio, 'w', zipfile.ZIP_STORED) as zipfp: + with zipfile2.ZipFile(bio, 'w', zipfile.ZIP_STORED) as zipfp: zipfp.write(TESTFN, TESTFN) zipfiledata = bio.getvalue() data = b'I am not a ZipFile!'*10 @@ -544,13 +546,13 @@ def test_read_concatenated_zip_file(self): f.write(data) f.write(zipfiledata) - with zipfile.ZipFile(TESTFN2) as zipfp: + with zipfile2.ZipFile(TESTFN2) as zipfp: self.assertEqual(zipfp.namelist(), [TESTFN]) self.assertEqual(zipfp.read(TESTFN), self.data) def test_append_to_concatenated_zip_file(self): with io.BytesIO() as bio: - with zipfile.ZipFile(bio, 'w', zipfile.ZIP_STORED) as zipfp: + with zipfile2.ZipFile(bio, 'w', zipfile.ZIP_STORED) as zipfp: zipfp.write(TESTFN, TESTFN) zipfiledata = bio.getvalue() data = b'I am not a ZipFile!'*1000000 @@ -558,50 +560,50 @@ def test_append_to_concatenated_zip_file(self): f.write(data) f.write(zipfiledata) - with zipfile.ZipFile(TESTFN2, 'a') as zipfp: + with zipfile2.ZipFile(TESTFN2, 'a') as zipfp: self.assertEqual(zipfp.namelist(), [TESTFN]) zipfp.writestr('strfile', self.data) with open(TESTFN2, 'rb') as f: self.assertEqual(f.read(len(data)), data) zipfiledata = f.read() - with io.BytesIO(zipfiledata) as bio, zipfile.ZipFile(bio) as zipfp: + with io.BytesIO(zipfiledata) as bio, zipfile2.ZipFile(bio) as zipfp: self.assertEqual(zipfp.namelist(), [TESTFN, 'strfile']) self.assertEqual(zipfp.read(TESTFN), self.data) self.assertEqual(zipfp.read('strfile'), self.data) def test_ignores_newline_at_end(self): - with zipfile.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED) as zipfp: + with zipfile2.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED) as zipfp: zipfp.write(TESTFN, TESTFN) with open(TESTFN2, 'a', encoding='utf-8') as f: f.write("\r\n\00\00\00") - with zipfile.ZipFile(TESTFN2, "r") as zipfp: - self.assertIsInstance(zipfp, zipfile.ZipFile) + with zipfile2.ZipFile(TESTFN2, "r") as zipfp: + self.assertIsInstance(zipfp, zipfile2.ZipFile) def test_ignores_stuff_appended_past_comments(self): - with zipfile.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED) as zipfp: + with zipfile2.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED) as zipfp: zipfp.comment = b"this is a comment" zipfp.write(TESTFN, TESTFN) with open(TESTFN2, 'a', encoding='utf-8') as f: f.write("abcdef\r\n") - with zipfile.ZipFile(TESTFN2, "r") as zipfp: - self.assertIsInstance(zipfp, zipfile.ZipFile) + with zipfile2.ZipFile(TESTFN2, "r") as zipfp: + self.assertIsInstance(zipfp, zipfile2.ZipFile) self.assertEqual(zipfp.comment, b"this is a comment") def test_write_default_name(self): """Check that calling ZipFile.write without arcname specified produces the expected result.""" - with zipfile.ZipFile(TESTFN2, "w") as zipfp: + with zipfile2.ZipFile(TESTFN2, "w") as zipfp: zipfp.write(TESTFN) with open(TESTFN, "rb") as f: self.assertEqual(zipfp.read(TESTFN), f.read()) def test_io_on_closed_zipextfile(self): fname = "somefile.txt" - with zipfile.ZipFile(TESTFN2, mode="w", compression=self.compression) as zipfp: + with zipfile2.ZipFile(TESTFN2, mode="w", compression=self.compression) as zipfp: zipfp.writestr(fname, "bogus") - with zipfile.ZipFile(TESTFN2, mode="r") as zipfp: + with zipfile2.ZipFile(TESTFN2, mode="r") as zipfp: with zipfp.open(fname) as fid: fid.close() self.assertIs(fid.closed, True) @@ -612,23 +614,23 @@ def test_io_on_closed_zipextfile(self): def test_write_to_readonly(self): """Check that trying to call write() on a readonly ZipFile object raises a ValueError.""" - with zipfile.ZipFile(TESTFN2, mode="w") as zipfp: + with zipfile2.ZipFile(TESTFN2, mode="w") as zipfp: zipfp.writestr("somefile.txt", "bogus") - with zipfile.ZipFile(TESTFN2, mode="r") as zipfp: + with zipfile2.ZipFile(TESTFN2, mode="r") as zipfp: self.assertRaises(ValueError, zipfp.write, TESTFN) - with zipfile.ZipFile(TESTFN2, mode="r") as zipfp: + with zipfile2.ZipFile(TESTFN2, mode="r") as zipfp: with self.assertRaises(ValueError): zipfp.open(TESTFN, mode='w') def test_add_file_before_1980(self): # Set atime and mtime to 1970-01-01 os.utime(TESTFN, (0, 0)) - with zipfile.ZipFile(TESTFN2, "w") as zipfp: + with zipfile2.ZipFile(TESTFN2, "w") as zipfp: self.assertRaises(ValueError, zipfp.write, TESTFN) - with zipfile.ZipFile(TESTFN2, "w", strict_timestamps=False) as zipfp: + with zipfile2.ZipFile(TESTFN2, "w", strict_timestamps=False) as zipfp: zipfp.write(TESTFN) zinfo = zipfp.getinfo(TESTFN) self.assertEqual(zinfo.date_time, (1980, 1, 1, 0, 0, 0)) @@ -657,10 +659,10 @@ def test_add_file_after_2107(self): # https://bugs.python.org/issue39460#msg360952 self.skipTest(f"Linux VFS/XFS kernel bug detected: {mtime_ns=}") - with zipfile.ZipFile(TESTFN2, "w") as zipfp: + with zipfile2.ZipFile(TESTFN2, "w") as zipfp: self.assertRaises(struct.error, zipfp.write, TESTFN) - with zipfile.ZipFile(TESTFN2, "w", strict_timestamps=False) as zipfp: + with zipfile2.ZipFile(TESTFN2, "w", strict_timestamps=False) as zipfp: zipfp.write(TESTFN) zinfo = zipfp.getinfo(TESTFN) self.assertEqual(zinfo.date_time, (2107, 12, 31, 23, 59, 59)) @@ -674,7 +676,7 @@ class DeflateTestsWithSourceFile(AbstractTestsWithSourceFile, def test_per_file_compression(self): """Check that files within a Zip archive can have different compression options.""" - with zipfile.ZipFile(TESTFN2, "w") as zipfp: + with zipfile2.ZipFile(TESTFN2, "w") as zipfp: zipfp.write(TESTFN, 'storeme', zipfile.ZIP_STORED) zipfp.write(TESTFN, 'deflateme', zipfile.ZIP_DEFLATED) sinfo = zipfp.getinfo('storeme') @@ -715,13 +717,13 @@ def setUp(self): def zip_test(self, f, compression): # Create the ZIP archive - with zipfile.ZipFile(f, "w", compression, allowZip64=True) as zipfp: + with zipfile2.ZipFile(f, "w", compression, allowZip64=True) as zipfp: zipfp.write(TESTFN, "another.name") zipfp.write(TESTFN, TESTFN) zipfp.writestr("strfile", self.data) # Read the ZIP archive - with zipfile.ZipFile(f, "r", compression) as zipfp: + with zipfile2.ZipFile(f, "r", compression) as zipfp: self.assertEqual(zipfp.read(TESTFN), self.data) self.assertEqual(zipfp.read("another.name"), self.data) self.assertEqual(zipfp.read("strfile"), self.data) @@ -777,7 +779,7 @@ def test_basic(self): def test_too_many_files(self): # This test checks that more than 64k files can be added to an archive, # and that the resulting archive can be read properly by ZipFile - zipf = zipfile.ZipFile(TESTFN, "w", self.compression, + zipf = zipfile2.ZipFile(TESTFN, "w", self.compression, allowZip64=True) zipf.debug = 100 numfiles = 15 @@ -786,7 +788,7 @@ def test_too_many_files(self): self.assertEqual(len(zipf.namelist()), numfiles) zipf.close() - zipf2 = zipfile.ZipFile(TESTFN, "r", self.compression) + zipf2 = zipfile2.ZipFile(TESTFN, "r", self.compression) self.assertEqual(len(zipf2.namelist()), numfiles) for i in range(numfiles): content = zipf2.read("foo%08d" % i).decode('ascii') @@ -794,7 +796,7 @@ def test_too_many_files(self): zipf2.close() def test_too_many_files_append(self): - zipf = zipfile.ZipFile(TESTFN, "w", self.compression, + zipf = zipfile2.ZipFile(TESTFN, "w", self.compression, allowZip64=False) zipf.debug = 100 numfiles = 9 @@ -806,7 +808,7 @@ def test_too_many_files_append(self): self.assertEqual(len(zipf.namelist()), numfiles) zipf.close() - zipf = zipfile.ZipFile(TESTFN, "a", self.compression, + zipf = zipfile2.ZipFile(TESTFN, "a", self.compression, allowZip64=False) zipf.debug = 100 self.assertEqual(len(zipf.namelist()), numfiles) @@ -815,7 +817,7 @@ def test_too_many_files_append(self): self.assertEqual(len(zipf.namelist()), numfiles) zipf.close() - zipf = zipfile.ZipFile(TESTFN, "a", self.compression, + zipf = zipfile2.ZipFile(TESTFN, "a", self.compression, allowZip64=True) zipf.debug = 100 self.assertEqual(len(zipf.namelist()), numfiles) @@ -825,7 +827,7 @@ def test_too_many_files_append(self): self.assertEqual(len(zipf.namelist()), numfiles2) zipf.close() - zipf2 = zipfile.ZipFile(TESTFN, "r", self.compression) + zipf2 = zipfile2.ZipFile(TESTFN, "r", self.compression) self.assertEqual(len(zipf2.namelist()), numfiles2) for i in range(numfiles2): content = zipf2.read("foo%08d" % i).decode('ascii') @@ -844,12 +846,12 @@ class StoredTestZip64InSmallFiles(AbstractTestZip64InSmallFiles, compression = zipfile.ZIP_STORED def large_file_exception_test(self, f, compression): - with zipfile.ZipFile(f, "w", compression, allowZip64=False) as zipfp: + with zipfile2.ZipFile(f, "w", compression, allowZip64=False) as zipfp: self.assertRaises(zipfile.LargeZipFile, zipfp.write, TESTFN, "another.name") def large_file_exception_test2(self, f, compression): - with zipfile.ZipFile(f, "w", compression, allowZip64=False) as zipfp: + with zipfile2.ZipFile(f, "w", compression, allowZip64=False) as zipfp: self.assertRaises(zipfile.LargeZipFile, zipfp.writestr, "another.name", self.data) @@ -859,24 +861,24 @@ def test_large_file_exception(self): self.large_file_exception_test2(f, zipfile.ZIP_STORED) def test_absolute_arcnames(self): - with zipfile.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED, + with zipfile2.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED, allowZip64=True) as zipfp: zipfp.write(TESTFN, "/absolute") - with zipfile.ZipFile(TESTFN2, "r", zipfile.ZIP_STORED) as zipfp: + with zipfile2.ZipFile(TESTFN2, "r", zipfile.ZIP_STORED) as zipfp: self.assertEqual(zipfp.namelist(), ["absolute"]) def test_append(self): # Test that appending to the Zip64 archive doesn't change # extra fields of existing entries. - with zipfile.ZipFile(TESTFN2, "w", allowZip64=True) as zipfp: + with zipfile2.ZipFile(TESTFN2, "w", allowZip64=True) as zipfp: zipfp.writestr("strfile", self.data) - with zipfile.ZipFile(TESTFN2, "r", allowZip64=True) as zipfp: + with zipfile2.ZipFile(TESTFN2, "r", allowZip64=True) as zipfp: zinfo = zipfp.getinfo("strfile") extra = zinfo.extra - with zipfile.ZipFile(TESTFN2, "a", allowZip64=True) as zipfp: + with zipfile2.ZipFile(TESTFN2, "a", allowZip64=True) as zipfp: zipfp.writestr("strfile2", self.data) - with zipfile.ZipFile(TESTFN2, "r", allowZip64=True) as zipfp: + with zipfile2.ZipFile(TESTFN2, "r", allowZip64=True) as zipfp: zinfo = zipfp.getinfo("strfile") self.assertEqual(zinfo.extra, extra) @@ -1001,7 +1003,7 @@ def test_bad_zip64_extra(self): file_size_64_set=True, ) with self.assertRaises(zipfile.BadZipFile) as e: - zipfile.ZipFile(io.BytesIO(missing_file_size_extra)) + zipfile2.ZipFile(io.BytesIO(missing_file_size_extra)) self.assertIn('file size', str(e.exception).lower()) # zip64 file size present, zip64 compress size present, one field in @@ -1012,7 +1014,7 @@ def test_bad_zip64_extra(self): compress_size_64_set=True, ) with self.assertRaises(zipfile.BadZipFile) as e: - zipfile.ZipFile(io.BytesIO(missing_compress_size_extra)) + zipfile2.ZipFile(io.BytesIO(missing_compress_size_extra)) self.assertIn('compress size', str(e.exception).lower()) # zip64 compress size present, no fields in extra, expecting one, @@ -1021,7 +1023,7 @@ def test_bad_zip64_extra(self): compress_size_64_set=True, ) with self.assertRaises(zipfile.BadZipFile) as e: - zipfile.ZipFile(io.BytesIO(missing_compress_size_extra)) + zipfile2.ZipFile(io.BytesIO(missing_compress_size_extra)) self.assertIn('compress size', str(e.exception).lower()) # zip64 file size present, zip64 compress size present, zip64 header @@ -1035,7 +1037,7 @@ def test_bad_zip64_extra(self): header_offset_64_set=True, ) with self.assertRaises(zipfile.BadZipFile) as e: - zipfile.ZipFile(io.BytesIO(missing_header_offset_extra)) + zipfile2.ZipFile(io.BytesIO(missing_header_offset_extra)) self.assertIn('header offset', str(e.exception).lower()) # zip64 compress size present, zip64 header offset present, one field @@ -1047,7 +1049,7 @@ def test_bad_zip64_extra(self): header_offset_64_set=True, ) with self.assertRaises(zipfile.BadZipFile) as e: - zipfile.ZipFile(io.BytesIO(missing_header_offset_extra)) + zipfile2.ZipFile(io.BytesIO(missing_header_offset_extra)) self.assertIn('header offset', str(e.exception).lower()) # zip64 file size present, zip64 header offset present, one field in @@ -1059,7 +1061,7 @@ def test_bad_zip64_extra(self): header_offset_64_set=True, ) with self.assertRaises(zipfile.BadZipFile) as e: - zipfile.ZipFile(io.BytesIO(missing_header_offset_extra)) + zipfile2.ZipFile(io.BytesIO(missing_header_offset_extra)) self.assertIn('header offset', str(e.exception).lower()) # zip64 header offset present, no fields in extra, expecting one, @@ -1070,7 +1072,7 @@ def test_bad_zip64_extra(self): header_offset_64_set=True, ) with self.assertRaises(zipfile.BadZipFile) as e: - zipfile.ZipFile(io.BytesIO(missing_header_offset_extra)) + zipfile2.ZipFile(io.BytesIO(missing_header_offset_extra)) self.assertIn('header offset', str(e.exception).lower()) def test_generated_valid_zip64_extra(self): @@ -1093,7 +1095,7 @@ def test_generated_valid_zip64_extra(self): kwargs = {} for c in combo: kwargs.update(c) - with zipfile.ZipFile(io.BytesIO(self.make_zip64_file(**kwargs))) as zf: + with zipfile2.ZipFile(io.BytesIO(self.make_zip64_file(**kwargs))) as zf: zinfo = zf.infolist()[0] self.assertEqual(zinfo.file_size, expected_file_size) self.assertEqual(zinfo.compress_size, expected_compress_size) @@ -1122,7 +1124,7 @@ def test_force_zip64(self): # - 4.5.3 for zip64 extra field data = io.BytesIO() - with zipfile.ZipFile(data, mode="w", allowZip64=True) as zf: + with zipfile2.ZipFile(data, mode="w", allowZip64=True) as zf: with zf.open("text.txt", mode="w", force_zip64=True) as zi: zi.write(b"_") @@ -1149,7 +1151,7 @@ def test_force_zip64(self): self.assertEqual(ex_csize, 1) # compressed size self.assertEqual(cd_sig, b"PK\x01\x02") # ensure the central directory header is next - z = zipfile.ZipFile(io.BytesIO(zipdata)) + z = zipfile2.ZipFile(io.BytesIO(zipdata)) zinfos = z.infolist() self.assertEqual(len(zinfos), 1) self.assertGreaterEqual(zinfos[0].extract_version, zipfile.ZIP64_VERSION) # requires zip64 to extract @@ -1158,7 +1160,7 @@ def test_unseekable_zip_unknown_filesize(self): """Test that creating a zip with/without seeking will raise a RuntimeError if zip64 was required but not used""" def make_zip(fp): - with zipfile.ZipFile(fp, mode="w", allowZip64=True) as zf: + with zipfile2.ZipFile(fp, mode="w", allowZip64=True) as zf: with zf.open("text.txt", mode="w", force_zip64=False) as zi: zi.write(b"_" * (zipfile.ZIP64_LIMIT + 1)) @@ -1168,7 +1170,7 @@ def make_zip(fp): def test_zip64_required_not_allowed_fail(self): """Test that trying to add a large file to a zip that doesn't allow zip64 extensions fails on add""" def make_zip(fp): - with zipfile.ZipFile(fp, mode="w", allowZip64=False) as zf: + with zipfile2.ZipFile(fp, mode="w", allowZip64=False) as zf: # pretend zipfile.ZipInfo.from_file was used to get the name and filesize info = zipfile.ZipInfo("text.txt") info.file_size = zipfile.ZIP64_LIMIT + 1 @@ -1196,7 +1198,7 @@ def test_unseekable_zip_known_filesize(self): file_size = zipfile.ZIP64_LIMIT + 1 def make_zip(fp): - with zipfile.ZipFile(fp, mode="w", allowZip64=True) as zf: + with zipfile2.ZipFile(fp, mode="w", allowZip64=True) as zf: # pretend zipfile.ZipInfo.from_file was used to get the name and filesize info = zipfile.ZipInfo("text.txt") info.file_size = file_size @@ -1277,7 +1279,7 @@ def tearDown(self): def test_close_after_close(self): data = b'content' - with zipfile.ZipFile(TESTFN2, "w", self.compression) as zipf: + with zipfile2.ZipFile(TESTFN2, "w", self.compression) as zipf: w = zipf.open('test', 'w') w.write(data) w.close() @@ -1288,7 +1290,7 @@ def test_close_after_close(self): def test_write_after_close(self): data = b'content' - with zipfile.ZipFile(TESTFN2, "w", self.compression) as zipf: + with zipfile2.ZipFile(TESTFN2, "w", self.compression) as zipf: w = zipf.open('test', 'w') w.write(data) w.close() @@ -1299,14 +1301,14 @@ def test_write_after_close(self): def test_issue44439(self): q = array.array('Q', [1, 2, 3, 4, 5]) LENGTH = len(q) * q.itemsize - with zipfile.ZipFile(io.BytesIO(), 'w', self.compression) as zip: + with zipfile2.ZipFile(io.BytesIO(), 'w', self.compression) as zip: with zip.open('data', 'w') as data: self.assertEqual(data.write(q), LENGTH) self.assertEqual(zip.getinfo('data').file_size, LENGTH) def test_zipwritefile_attrs(self): fname = "somefile.txt" - with zipfile.ZipFile(TESTFN2, mode="w", compression=self.compression) as zipfp: + with zipfile2.ZipFile(TESTFN2, mode="w", compression=self.compression) as zipfp: with zipfp.open(fname, 'w') as fid: self.assertRaises(io.UnsupportedOperation, fid.fileno) self.assertIs(fid.readable(), False) @@ -1338,14 +1340,14 @@ class LzmaWriterTests(AbstractWriterTests, unittest.TestCase): class ExtractTests(unittest.TestCase): def make_test_file(self): - with zipfile.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED) as zipfp: + with zipfile2.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED) as zipfp: for fpath, fdata in SMALL_TEST_DATA: zipfp.writestr(fpath, fdata) def test_extract(self): with temp_cwd(): self.make_test_file() - with zipfile.ZipFile(TESTFN2, "r") as zipfp: + with zipfile2.ZipFile(TESTFN2, "r") as zipfp: for fpath, fdata in SMALL_TEST_DATA: writtenfile = zipfp.extract(fpath) @@ -1363,7 +1365,7 @@ def test_extract(self): def _test_extract_with_target(self, target): self.make_test_file() - with zipfile.ZipFile(TESTFN2, "r") as zipfp: + with zipfile2.ZipFile(TESTFN2, "r") as zipfp: for fpath, fdata in SMALL_TEST_DATA: writtenfile = zipfp.extract(fpath, target) @@ -1391,7 +1393,7 @@ def test_extract_with_target_pathlike(self): def test_extract_all(self): with temp_cwd(): self.make_test_file() - with zipfile.ZipFile(TESTFN2, "r") as zipfp: + with zipfile2.ZipFile(TESTFN2, "r") as zipfp: zipfp.extractall() for fpath, fdata in SMALL_TEST_DATA: outfile = os.path.join(os.getcwd(), fpath) @@ -1403,7 +1405,7 @@ def test_extract_all(self): def _test_extract_all_with_target(self, target): self.make_test_file() - with zipfile.ZipFile(TESTFN2, "r") as zipfp: + with zipfile2.ZipFile(TESTFN2, "r") as zipfp: zipfp.extractall(target) for fpath, fdata in SMALL_TEST_DATA: outfile = os.path.join(target, fpath) @@ -1429,7 +1431,7 @@ def check_file(self, filename, content): self.assertEqual(f.read(), content) def test_sanitize_windows_name(self): - san = zipfile.ZipFile._sanitize_windows_name + san = zipfile2.ZipFile._sanitize_windows_name # Passing pathsep in allows this test to work regardless of platform. self.assertEqual(san(r',,?,C:,foo,bar/z', ','), r'_,C_,foo,bar/z') self.assertEqual(san(r'a\b,ce|f"g?h*i', ','), r'a\b,c_d_e_f_g_h_i') @@ -1488,7 +1490,7 @@ def test_extract_hackers_arcnames_posix_only(self): def _test_extract_hackers_arcnames(self, hacknames): for arcname, fixedname in hacknames: content = b'foobar' + arcname.encode() - with zipfile.ZipFile(TESTFN2, 'w', zipfile.ZIP_STORED) as zipfp: + with zipfile2.ZipFile(TESTFN2, 'w', zipfile.ZIP_STORED) as zipfp: zinfo = zipfile.ZipInfo() # preserve backslashes zinfo.filename = arcname @@ -1499,7 +1501,7 @@ def _test_extract_hackers_arcnames(self, hacknames): targetpath = os.path.join('target', 'subdir', 'subsub') correctfile = os.path.join(targetpath, *fixedname.split('/')) - with zipfile.ZipFile(TESTFN2, 'r') as zipfp: + with zipfile2.ZipFile(TESTFN2, 'r') as zipfp: writtenfile = zipfp.extract(arcname, targetpath) self.assertEqual(writtenfile, correctfile, msg='extract %r: %r != %r' % @@ -1507,21 +1509,21 @@ def _test_extract_hackers_arcnames(self, hacknames): self.check_file(correctfile, content) rmtree('target') - with zipfile.ZipFile(TESTFN2, 'r') as zipfp: + with zipfile2.ZipFile(TESTFN2, 'r') as zipfp: zipfp.extractall(targetpath) self.check_file(correctfile, content) rmtree('target') correctfile = os.path.join(os.getcwd(), *fixedname.split('/')) - with zipfile.ZipFile(TESTFN2, 'r') as zipfp: + with zipfile2.ZipFile(TESTFN2, 'r') as zipfp: writtenfile = zipfp.extract(arcname) self.assertEqual(writtenfile, correctfile, msg="extract %r" % arcname) self.check_file(correctfile, content) rmtree(fixedname.split('/')[0]) - with zipfile.ZipFile(TESTFN2, 'r') as zipfp: + with zipfile2.ZipFile(TESTFN2, 'r') as zipfp: zipfp.extractall() self.check_file(correctfile, content) rmtree(fixedname.split('/')[0]) @@ -1536,21 +1538,21 @@ class OverwriteTests(archiver_tests.OverwriteTests, unittest.TestCase): def setUpClass(cls): p = cls.ar_with_file = TESTFN + '-with-file.zip' cls.addClassCleanup(unlink, p) - with zipfile.ZipFile(p, 'w') as zipfp: + with zipfile2.ZipFile(p, 'w') as zipfp: zipfp.writestr('test', b'newcontent') p = cls.ar_with_dir = TESTFN + '-with-dir.zip' cls.addClassCleanup(unlink, p) - with zipfile.ZipFile(p, 'w') as zipfp: + with zipfile2.ZipFile(p, 'w') as zipfp: zipfp.mkdir('test') p = cls.ar_with_implicit_dir = TESTFN + '-with-implicit-dir.zip' cls.addClassCleanup(unlink, p) - with zipfile.ZipFile(p, 'w') as zipfp: + with zipfile2.ZipFile(p, 'w') as zipfp: zipfp.writestr('test/file', b'newcontent') def open(self, path): - return zipfile.ZipFile(path, 'r') + return zipfile2.ZipFile(path, 'r') def extractall(self, ar): ar.extractall(self.testdir) @@ -1559,13 +1561,13 @@ def extractall(self, ar): class OtherTests(unittest.TestCase): def test_open_via_zip_info(self): # Create the ZIP archive - with zipfile.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED) as zipfp: + with zipfile2.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED) as zipfp: zipfp.writestr("name", "foo") with self.assertWarns(UserWarning): zipfp.writestr("name", "bar") self.assertEqual(zipfp.namelist(), ["name"] * 2) - with zipfile.ZipFile(TESTFN2, "r") as zipfp: + with zipfile2.ZipFile(TESTFN2, "r") as zipfp: infos = zipfp.infolist() data = b"" for info in infos: @@ -1578,7 +1580,7 @@ def test_open_via_zip_info(self): self.assertIn(data, {b"foobar", b"barfoo"}) def test_writestr_extended_local_header_issue1202(self): - with zipfile.ZipFile(TESTFN2, 'w') as orig_zip: + with zipfile2.ZipFile(TESTFN2, 'w') as orig_zip: for data in 'abcdefghijklmnop': zinfo = zipfile.ZipInfo(data) zinfo.flag_bits |= zipfile._MASK_USE_DATA_DESCRIPTOR # Include an extended local header. @@ -1586,25 +1588,25 @@ def test_writestr_extended_local_header_issue1202(self): def test_close(self): """Check that the zipfile is closed after the 'with' block.""" - with zipfile.ZipFile(TESTFN2, "w") as zipfp: + with zipfile2.ZipFile(TESTFN2, "w") as zipfp: for fpath, fdata in SMALL_TEST_DATA: zipfp.writestr(fpath, fdata) self.assertIsNotNone(zipfp.fp, 'zipfp is not open') self.assertIsNone(zipfp.fp, 'zipfp is not closed') - with zipfile.ZipFile(TESTFN2, "r") as zipfp: + with zipfile2.ZipFile(TESTFN2, "r") as zipfp: self.assertIsNotNone(zipfp.fp, 'zipfp is not open') self.assertIsNone(zipfp.fp, 'zipfp is not closed') def test_close_on_exception(self): """Check that the zipfile is closed if an exception is raised in the 'with' block.""" - with zipfile.ZipFile(TESTFN2, "w") as zipfp: + with zipfile2.ZipFile(TESTFN2, "w") as zipfp: for fpath, fdata in SMALL_TEST_DATA: zipfp.writestr(fpath, fdata) try: - with zipfile.ZipFile(TESTFN2, "r") as zipfp2: + with zipfile2.ZipFile(TESTFN2, "r") as zipfp2: raise zipfile.BadZipFile() except zipfile.BadZipFile: self.assertIsNone(zipfp2.fp, 'zipfp is not closed') @@ -1617,29 +1619,29 @@ def test_unsupported_version(self): b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x01\x00\x00\x00\x00xPK\x05\x06' b'\x00\x00\x00\x00\x01\x00\x01\x00/\x00\x00\x00\x1f\x00\x00\x00\x00\x00') - self.assertRaises(NotImplementedError, zipfile.ZipFile, + self.assertRaises(NotImplementedError, zipfile2.ZipFile, io.BytesIO(data), 'r') @requires_zlib() def test_read_unicode_filenames(self): # bug #10801 fname = findfile('zip_cp437_header.zip') - with zipfile.ZipFile(fname) as zipfp: + with zipfile2.ZipFile(fname) as zipfp: for name in zipfp.namelist(): zipfp.open(name).close() def test_write_unicode_filenames(self): - with zipfile.ZipFile(TESTFN, "w") as zf: + with zipfile2.ZipFile(TESTFN, "w") as zf: zf.writestr("foo.txt", "Test for unicode filename") zf.writestr("\xf6.txt", "Test for unicode filename") self.assertIsInstance(zf.infolist()[0].filename, str) - with zipfile.ZipFile(TESTFN, "r") as zf: + with zipfile2.ZipFile(TESTFN, "r") as zf: self.assertEqual(zf.filelist[0].filename, "foo.txt") self.assertEqual(zf.filelist[1].filename, "\xf6.txt") def create_zipfile_with_extra_data(self, filename, extra_data_name): - with zipfile.ZipFile(TESTFN, mode='w') as zf: + with zipfile2.ZipFile(TESTFN, mode='w') as zf: filename_encoded = filename.encode("utf-8") # create a ZipInfo object with Unicode path extra field zip_info = zipfile.ZipInfo(filename) @@ -1661,23 +1663,23 @@ def create_zipfile_with_extra_data(self, filename, extra_data_name): @requires_zlib() def test_read_zipfile_containing_unicode_path_extra_field(self): self.create_zipfile_with_extra_data("이름.txt", "이름.txt".encode("utf-8")) - with zipfile.ZipFile(TESTFN, "r") as zf: + with zipfile2.ZipFile(TESTFN, "r") as zf: self.assertEqual(zf.filelist[0].filename, "이름.txt") @requires_zlib() def test_read_zipfile_warning(self): self.create_zipfile_with_extra_data("이름.txt", b"") with self.assertWarns(UserWarning): - zipfile.ZipFile(TESTFN, "r").close() + zipfile2.ZipFile(TESTFN, "r").close() @requires_zlib() def test_read_zipfile_error(self): self.create_zipfile_with_extra_data("이름.txt", b"\xff") with self.assertRaises(zipfile.BadZipfile): - zipfile.ZipFile(TESTFN, "r").close() + zipfile2.ZipFile(TESTFN, "r").close() def test_read_after_write_unicode_filenames(self): - with zipfile.ZipFile(TESTFN2, 'w') as zipfp: + with zipfile2.ZipFile(TESTFN2, 'w') as zipfp: zipfp.writestr('приклад', b'sample') self.assertEqual(zipfp.read('приклад'), b'sample') @@ -1686,11 +1688,11 @@ def test_exclusive_create_zip_file(self): unlink(TESTFN2) filename = 'testfile.txt' content = b'hello, world. this is some content.' - with zipfile.ZipFile(TESTFN2, "x", zipfile.ZIP_STORED) as zipfp: + with zipfile2.ZipFile(TESTFN2, "x", zipfile.ZIP_STORED) as zipfp: zipfp.writestr(filename, content) with self.assertRaises(FileExistsError): - zipfile.ZipFile(TESTFN2, "x", zipfile.ZIP_STORED) - with zipfile.ZipFile(TESTFN2, "r") as zipfp: + zipfile2.ZipFile(TESTFN2, "x", zipfile.ZIP_STORED) + with zipfile2.ZipFile(TESTFN2, "r") as zipfp: self.assertEqual(zipfp.namelist(), [filename]) self.assertEqual(zipfp.read(filename), content) @@ -1702,14 +1704,14 @@ def test_create_non_existent_file_for_append(self): content = b'hello, world. this is some content.' try: - with zipfile.ZipFile(TESTFN, 'a') as zf: + with zipfile2.ZipFile(TESTFN, 'a') as zf: zf.writestr(filename, content) except OSError: self.fail('Could not append data to a non-existent zip file.') self.assertTrue(os.path.exists(TESTFN)) - with zipfile.ZipFile(TESTFN, 'r') as zf: + with zipfile2.ZipFile(TESTFN, 'r') as zf: self.assertEqual(zf.read(filename), content) def test_close_erroneous_file(self): @@ -1723,7 +1725,7 @@ def test_close_erroneous_file(self): with open(TESTFN, "w", encoding="utf-8") as fp: fp.write("this is not a legal zip file\n") try: - zf = zipfile.ZipFile(TESTFN) + zf = zipfile2.ZipFile(TESTFN) except zipfile.BadZipFile: pass @@ -1749,7 +1751,7 @@ def test_damaged_zipfile(self): """Check that zipfiles with missing bytes at the end raise BadZipFile.""" # - Create a valid zip file fp = io.BytesIO() - with zipfile.ZipFile(fp, mode="w") as zipf: + with zipfile2.ZipFile(fp, mode="w") as zipf: zipf.writestr("foo.txt", b"O, for a Muse of Fire!") zipfiledata = fp.getvalue() @@ -1757,12 +1759,12 @@ def test_damaged_zipfile(self): # a BadZipFile exception is raised when we try to open it for N in range(len(zipfiledata)): fp = io.BytesIO(zipfiledata[:N]) - self.assertRaises(zipfile.BadZipFile, zipfile.ZipFile, fp) + self.assertRaises(zipfile.BadZipFile, zipfile2.ZipFile, fp) def test_is_zip_valid_file(self): """Check that is_zipfile() correctly identifies zip files.""" # - passing a filename - with zipfile.ZipFile(TESTFN, mode="w") as zipf: + with zipfile2.ZipFile(TESTFN, mode="w") as zipf: zipf.writestr("foo.txt", b"O, for a Muse of Fire!") self.assertTrue(zipfile.is_zipfile(TESTFN)) @@ -1790,16 +1792,16 @@ def test_non_existent_file_raises_OSError(self): # it is ignored, but the user should be sufficiently annoyed by # the message on the output that regression will be noticed # quickly. - self.assertRaises(OSError, zipfile.ZipFile, TESTFN) + self.assertRaises(OSError, zipfile2.ZipFile, TESTFN) def test_empty_file_raises_BadZipFile(self): f = open(TESTFN, 'w', encoding='utf-8') f.close() - self.assertRaises(zipfile.BadZipFile, zipfile.ZipFile, TESTFN) + self.assertRaises(zipfile.BadZipFile, zipfile2.ZipFile, TESTFN) with open(TESTFN, 'w', encoding='utf-8') as fp: fp.write("short file") - self.assertRaises(zipfile.BadZipFile, zipfile.ZipFile, TESTFN) + self.assertRaises(zipfile.BadZipFile, zipfile2.ZipFile, TESTFN) def test_negative_central_directory_offset_raises_BadZipFile(self): # Zip file containing an empty EOCD record @@ -1810,12 +1812,12 @@ def test_negative_central_directory_offset_raises_BadZipFile(self): for dirsize in 1, 2**32-1: buffer[12:16] = struct.pack(' os.path.getsize(TESTFN)) - with zipfile.ZipFile(TESTFN,mode="r") as zipf: + with zipfile2.ZipFile(TESTFN,mode="r") as zipf: self.assertEqual(zipf.comment, b"shorter comment") def test_unicode_comment(self): - with zipfile.ZipFile(TESTFN, "w", zipfile.ZIP_STORED) as zipf: + with zipfile2.ZipFile(TESTFN, "w", zipfile.ZIP_STORED) as zipf: zipf.writestr("foo.txt", "O, for a Muse of Fire!") with self.assertRaises(TypeError): zipf.comment = "this is an error" def test_change_comment_in_empty_archive(self): - with zipfile.ZipFile(TESTFN, "a", zipfile.ZIP_STORED) as zipf: + with zipfile2.ZipFile(TESTFN, "a", zipfile.ZIP_STORED) as zipf: self.assertFalse(zipf.filelist) zipf.comment = b"this is a comment" - with zipfile.ZipFile(TESTFN, "r") as zipf: + with zipfile2.ZipFile(TESTFN, "r") as zipf: self.assertEqual(zipf.comment, b"this is a comment") def test_change_comment_in_nonempty_archive(self): - with zipfile.ZipFile(TESTFN, "w", zipfile.ZIP_STORED) as zipf: + with zipfile2.ZipFile(TESTFN, "w", zipfile.ZIP_STORED) as zipf: zipf.writestr("foo.txt", "O, for a Muse of Fire!") - with zipfile.ZipFile(TESTFN, "a", zipfile.ZIP_STORED) as zipf: + with zipfile2.ZipFile(TESTFN, "a", zipfile.ZIP_STORED) as zipf: self.assertTrue(zipf.filelist) zipf.comment = b"this is a comment" - with zipfile.ZipFile(TESTFN, "r") as zipf: + with zipfile2.ZipFile(TESTFN, "r") as zipf: self.assertEqual(zipf.comment, b"this is a comment") def test_empty_zipfile(self): # Check that creating a file in 'w' or 'a' mode and closing without # adding any files to the archives creates a valid empty ZIP file - zipf = zipfile.ZipFile(TESTFN, mode="w") + zipf = zipfile2.ZipFile(TESTFN, mode="w") zipf.close() try: - zipf = zipfile.ZipFile(TESTFN, mode="r") + zipf = zipfile2.ZipFile(TESTFN, mode="r") except zipfile.BadZipFile: self.fail("Unable to create empty ZIP file in 'w' mode") - zipf = zipfile.ZipFile(TESTFN, mode="a") + zipf = zipfile2.ZipFile(TESTFN, mode="a") zipf.close() try: - zipf = zipfile.ZipFile(TESTFN, mode="r") + zipf = zipfile2.ZipFile(TESTFN, mode="r") except: self.fail("Unable to create empty ZIP file in 'a' mode") @@ -1998,7 +2000,7 @@ def test_open_empty_file(self): # OSError) f = open(TESTFN, 'w', encoding='utf-8') f.close() - self.assertRaises(zipfile.BadZipFile, zipfile.ZipFile, TESTFN, 'r') + self.assertRaises(zipfile.BadZipFile, zipfile2.ZipFile, TESTFN, 'r') def test_create_zipinfo_before_1980(self): self.assertRaises(ValueError, @@ -2042,7 +2044,7 @@ def test_zipfile_with_short_extra_field(self): b'\x00\x00\x00abc\x00\x00PK\x05\x06\x00\x00\x00\x00' b'\x01\x00\x01\x003\x00\x00\x00%\x00\x00\x00\x00\x00' ) - with zipfile.ZipFile(io.BytesIO(zipdata), 'r') as zipf: + with zipfile2.ZipFile(io.BytesIO(zipdata), 'r') as zipf: # testzip returns the name of the first corrupt file, or None self.assertIsNone(zipf.testzip()) @@ -2051,7 +2053,7 @@ def test_open_conflicting_handles(self): msg1 = b"It's fun to charter an accountant!" msg2 = b"And sail the wide accountant sea" msg3 = b"To find, explore the funds offshore" - with zipfile.ZipFile(TESTFN2, 'w', zipfile.ZIP_STORED) as zipf: + with zipfile2.ZipFile(TESTFN2, 'w', zipfile.ZIP_STORED) as zipf: with zipf.open('foo', mode='w') as w2: w2.write(msg1) with zipf.open('bar', mode='w') as w1: @@ -2069,7 +2071,7 @@ def test_open_conflicting_handles(self): with zipf.open('baz', mode='w') as w2: w2.write(msg3) - with zipfile.ZipFile(TESTFN2, 'r') as zipf: + with zipfile2.ZipFile(TESTFN2, 'r') as zipf: self.assertEqual(zipf.read('foo'), msg1) self.assertEqual(zipf.read('bar'), msg2) self.assertEqual(zipf.read('baz'), msg3) @@ -2080,9 +2082,9 @@ def test_seek_tell(self): txt = b"Where's Bruce?" bloc = txt.find(b"Bruce") # Check seek on a file - with zipfile.ZipFile(TESTFN, "w") as zipf: + with zipfile2.ZipFile(TESTFN, "w") as zipf: zipf.writestr("foo.txt", txt) - with zipfile.ZipFile(TESTFN, "r") as zipf: + with zipfile2.ZipFile(TESTFN, "r") as zipf: with zipf.open("foo.txt", "r") as fp: fp.seek(bloc, os.SEEK_SET) self.assertEqual(fp.tell(), bloc) @@ -2098,9 +2100,9 @@ def test_seek_tell(self): self.assertEqual(fp.tell(), 0) # Check seek on memory file data = io.BytesIO() - with zipfile.ZipFile(data, mode="w") as zipf: + with zipfile2.ZipFile(data, mode="w") as zipf: zipf.writestr("foo.txt", txt) - with zipfile.ZipFile(data, mode="r") as zipf: + with zipfile2.ZipFile(data, mode="r") as zipf: with zipf.open("foo.txt", "r") as fp: fp.seek(bloc, os.SEEK_SET) self.assertEqual(fp.tell(), bloc) @@ -2119,13 +2121,13 @@ def test_read_after_seek(self): # Issue 102956: Make sure seek(x, os.SEEK_CUR) doesn't break read() txt = b"Charge men!" bloc = txt.find(b"men") - with zipfile.ZipFile(TESTFN, "w") as zipf: + with zipfile2.ZipFile(TESTFN, "w") as zipf: zipf.writestr("foo.txt", txt) - with zipfile.ZipFile(TESTFN, mode="r") as zipf: + with zipfile2.ZipFile(TESTFN, mode="r") as zipf: with zipf.open("foo.txt", "r") as fp: fp.seek(bloc, os.SEEK_CUR) self.assertEqual(fp.read(-1), b'men!') - with zipfile.ZipFile(TESTFN, mode="r") as zipf: + with zipfile2.ZipFile(TESTFN, mode="r") as zipf: with zipf.open("foo.txt", "r") as fp: fp.read(6) fp.seek(1, os.SEEK_CUR) @@ -2135,10 +2137,10 @@ def test_read_after_seek(self): def test_decompress_without_3rd_party_library(self): data = b'PK\x05\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' zip_file = io.BytesIO(data) - with zipfile.ZipFile(zip_file, 'w', compression=zipfile.ZIP_BZIP2) as zf: + with zipfile2.ZipFile(zip_file, 'w', compression=zipfile.ZIP_BZIP2) as zf: zf.writestr('a.txt', b'a') with mock.patch('zipfile.bz2', None): - with zipfile.ZipFile(zip_file) as zf: + with zipfile2.ZipFile(zip_file) as zf: self.assertRaises(RuntimeError, zf.extract, 'a.txt') @requires_zlib() @@ -2156,7 +2158,7 @@ def test_full_overlap(self): b'\x06\x00\x00\x00\x00\x02\x00\x02\x00^\x00\x00\x00/\x00\x00' b'\x00\x00\x00' ) - with zipfile.ZipFile(io.BytesIO(data), 'r') as zipf: + with zipfile2.ZipFile(io.BytesIO(data), 'r') as zipf: self.assertEqual(zipf.namelist(), ['a', 'b']) zi = zipf.getinfo('a') self.assertEqual(zi.header_offset, 0) @@ -2187,7 +2189,7 @@ def test_quoted_overlap(self): b'bPK\x05\x06\x00\x00\x00\x00\x02\x00\x02\x00^\x00\x00' b'\x00S\x00\x00\x00\x00\x00' ) - with zipfile.ZipFile(io.BytesIO(data), 'r') as zipf: + with zipfile2.ZipFile(io.BytesIO(data), 'r') as zipf: self.assertEqual(zipf.namelist(), ['a', 'b']) zi = zipf.getinfo('a') self.assertEqual(zi.header_offset, 0) @@ -2211,7 +2213,7 @@ def test_testzip_with_bad_crc(self): """Tests that files with bad CRCs return their name from testzip.""" zipdata = self.zip_with_bad_crc - with zipfile.ZipFile(io.BytesIO(zipdata), mode="r") as zipf: + with zipfile2.ZipFile(io.BytesIO(zipdata), mode="r") as zipf: # testzip returns the name of the first corrupt file, or None self.assertEqual('afile', zipf.testzip()) @@ -2220,16 +2222,16 @@ def test_read_with_bad_crc(self): zipdata = self.zip_with_bad_crc # Using ZipFile.read() - with zipfile.ZipFile(io.BytesIO(zipdata), mode="r") as zipf: + with zipfile2.ZipFile(io.BytesIO(zipdata), mode="r") as zipf: self.assertRaises(zipfile.BadZipFile, zipf.read, 'afile') # Using ZipExtFile.read() - with zipfile.ZipFile(io.BytesIO(zipdata), mode="r") as zipf: + with zipfile2.ZipFile(io.BytesIO(zipdata), mode="r") as zipf: with zipf.open('afile', 'r') as corrupt_file: self.assertRaises(zipfile.BadZipFile, corrupt_file.read) # Same with small reads (in order to exercise the buffering logic) - with zipfile.ZipFile(io.BytesIO(zipdata), mode="r") as zipf: + with zipfile2.ZipFile(io.BytesIO(zipdata), mode="r") as zipf: with zipf.open('afile', 'r') as corrupt_file: corrupt_file.MIN_READ_SIZE = 2 with self.assertRaises(zipfile.BadZipFile): @@ -2322,10 +2324,10 @@ class DecryptionTests(unittest.TestCase): def setUp(self): with open(TESTFN, "wb") as fp: fp.write(self.data) - self.zip = zipfile.ZipFile(TESTFN, "r") + self.zip = zipfile2.ZipFile(TESTFN, "r") with open(TESTFN2, "wb") as fp: fp.write(self.data2) - self.zip2 = zipfile.ZipFile(TESTFN2, "r") + self.zip2 = zipfile2.ZipFile(TESTFN2, "r") def tearDown(self): self.zip.close() @@ -2427,7 +2429,7 @@ def tearDown(self): def make_test_archive(self, f, compression): # Create the ZIP archive - with zipfile.ZipFile(f, "w", compression) as zipfp: + with zipfile2.ZipFile(f, "w", compression) as zipfp: zipfp.write(TESTFN, "another.name") zipfp.write(TESTFN, TESTFN) @@ -2435,7 +2437,7 @@ def zip_test(self, f, compression): self.make_test_archive(f, compression) # Read the ZIP archive - with zipfile.ZipFile(f, "r", compression) as zipfp: + with zipfile2.ZipFile(f, "r", compression) as zipfp: testdata = zipfp.read(TESTFN) self.assertEqual(len(testdata), len(self.data)) self.assertEqual(testdata, self.data) @@ -2449,7 +2451,7 @@ def zip_open_test(self, f, compression): self.make_test_archive(f, compression) # Read the ZIP archive - with zipfile.ZipFile(f, "r", compression) as zipfp: + with zipfile2.ZipFile(f, "r", compression) as zipfp: zipdata1 = [] with zipfp.open(TESTFN) as zipopen1: while True: @@ -2482,7 +2484,7 @@ def zip_random_open_test(self, f, compression): self.make_test_archive(f, compression) # Read the ZIP archive - with zipfile.ZipFile(f, "r", compression) as zipfp: + with zipfile2.ZipFile(f, "r", compression) as zipfp: zipdata1 = [] with zipfp.open(TESTFN) as zipopen1: while True: @@ -2554,11 +2556,11 @@ def test_writestr(self): f = io.BytesIO() f.write(b'abc') bf = io.BufferedWriter(f) - with zipfile.ZipFile(wrapper(bf), 'w', zipfile.ZIP_STORED) as zipfp: + with zipfile2.ZipFile(wrapper(bf), 'w', zipfile.ZIP_STORED) as zipfp: zipfp.writestr('ones', b'111') zipfp.writestr('twos', b'222') self.assertEqual(f.getvalue()[:5], b'abcPK') - with zipfile.ZipFile(f, mode='r') as zipf: + with zipfile2.ZipFile(f, mode='r') as zipf: with zipf.open('ones') as zopen: self.assertEqual(zopen.read(), b'111') with zipf.open('twos') as zopen: @@ -2570,7 +2572,7 @@ def test_write(self): f = io.BytesIO() f.write(b'abc') bf = io.BufferedWriter(f) - with zipfile.ZipFile(wrapper(bf), 'w', zipfile.ZIP_STORED) as zipfp: + with zipfile2.ZipFile(wrapper(bf), 'w', zipfile.ZIP_STORED) as zipfp: self.addCleanup(unlink, TESTFN) with open(TESTFN, 'wb') as f2: f2.write(b'111') @@ -2579,7 +2581,7 @@ def test_write(self): f2.write(b'222') zipfp.write(TESTFN, 'twos') self.assertEqual(f.getvalue()[:5], b'abcPK') - with zipfile.ZipFile(f, mode='r') as zipf: + with zipfile2.ZipFile(f, mode='r') as zipf: with zipf.open('ones') as zopen: self.assertEqual(zopen.read(), b'111') with zipf.open('twos') as zopen: @@ -2591,13 +2593,13 @@ def test_open_write(self): f = io.BytesIO() f.write(b'abc') bf = io.BufferedWriter(f) - with zipfile.ZipFile(wrapper(bf), 'w', zipfile.ZIP_STORED) as zipf: + with zipfile2.ZipFile(wrapper(bf), 'w', zipfile.ZIP_STORED) as zipf: with zipf.open('ones', 'w') as zopen: zopen.write(b'111') with zipf.open('twos', 'w') as zopen: zopen.write(b'222') self.assertEqual(f.getvalue()[:5], b'abcPK') - with zipfile.ZipFile(f) as zipf: + with zipfile2.ZipFile(f) as zipf: self.assertEqual(zipf.read('ones'), b'111') self.assertEqual(zipf.read('twos'), b'222') @@ -2611,7 +2613,7 @@ def setUpClass(cls): def make_test_archive(self, f): # Create the ZIP archive - with zipfile.ZipFile(f, "w", zipfile.ZIP_DEFLATED) as zipfp: + with zipfile2.ZipFile(f, "w", zipfile.ZIP_DEFLATED) as zipfp: zipfp.writestr('ones', self.data1) zipfp.writestr('twos', self.data2) @@ -2620,7 +2622,7 @@ def test_same_file(self): # multiple open() calls can be made without interfering with each other. for f in get_files(self): self.make_test_archive(f) - with zipfile.ZipFile(f, mode="r") as zipf: + with zipfile2.ZipFile(f, mode="r") as zipf: with zipf.open('ones') as zopen1, zipf.open('ones') as zopen2: data1 = zopen1.read(500) data2 = zopen2.read(500) @@ -2634,7 +2636,7 @@ def test_different_file(self): # multiple open() calls can be made without interfering with each other. for f in get_files(self): self.make_test_archive(f) - with zipfile.ZipFile(f, mode="r") as zipf: + with zipfile2.ZipFile(f, mode="r") as zipf: with zipf.open('ones') as zopen1, zipf.open('twos') as zopen2: data1 = zopen1.read(500) data2 = zopen2.read(500) @@ -2648,7 +2650,7 @@ def test_interleaved(self): # multiple open() calls can be made without interfering with each other. for f in get_files(self): self.make_test_archive(f) - with zipfile.ZipFile(f, mode="r") as zipf: + with zipfile2.ZipFile(f, mode="r") as zipf: with zipf.open('ones') as zopen1: data1 = zopen1.read(500) with zipf.open('twos') as zopen2: @@ -2662,7 +2664,7 @@ def test_read_after_close(self): for f in get_files(self): self.make_test_archive(f) with contextlib.ExitStack() as stack: - with zipfile.ZipFile(f, 'r') as zipf: + with zipfile2.ZipFile(f, 'r') as zipf: zopen1 = stack.enter_context(zipf.open('ones')) zopen2 = stack.enter_context(zipf.open('twos')) data1 = zopen1.read(500) @@ -2674,13 +2676,13 @@ def test_read_after_close(self): def test_read_after_write(self): for f in get_files(self): - with zipfile.ZipFile(f, 'w', zipfile.ZIP_DEFLATED) as zipf: + with zipfile2.ZipFile(f, 'w', zipfile.ZIP_DEFLATED) as zipf: zipf.writestr('ones', self.data1) zipf.writestr('twos', self.data2) with zipf.open('ones') as zopen1: data1 = zopen1.read(500) self.assertEqual(data1, self.data1[:500]) - with zipfile.ZipFile(f, 'r') as zipf: + with zipfile2.ZipFile(f, 'r') as zipf: data1 = zipf.read('ones') data2 = zipf.read('twos') self.assertEqual(data1, self.data1) @@ -2688,12 +2690,12 @@ def test_read_after_write(self): def test_write_after_read(self): for f in get_files(self): - with zipfile.ZipFile(f, "w", zipfile.ZIP_DEFLATED) as zipf: + with zipfile2.ZipFile(f, "w", zipfile.ZIP_DEFLATED) as zipf: zipf.writestr('ones', self.data1) with zipf.open('ones') as zopen1: zopen1.read(500) zipf.writestr('twos', self.data2) - with zipfile.ZipFile(f, 'r') as zipf: + with zipfile2.ZipFile(f, 'r') as zipf: data1 = zipf.read('ones') data2 = zipf.read('twos') self.assertEqual(data1, self.data1) @@ -2704,7 +2706,7 @@ def test_many_opens(self): # and don't rely on the garbage collector to free resources. startcount = fd_count() self.make_test_archive(TESTFN2) - with zipfile.ZipFile(TESTFN2, mode="r") as zipf: + with zipfile2.ZipFile(TESTFN2, mode="r") as zipf: for x in range(100): zipf.read('ones') with zipf.open('ones') as zopen1: @@ -2712,16 +2714,16 @@ def test_many_opens(self): self.assertEqual(startcount, fd_count()) def test_write_while_reading(self): - with zipfile.ZipFile(TESTFN2, 'w', zipfile.ZIP_DEFLATED) as zipf: + with zipfile2.ZipFile(TESTFN2, 'w', zipfile.ZIP_DEFLATED) as zipf: zipf.writestr('ones', self.data1) - with zipfile.ZipFile(TESTFN2, 'a', zipfile.ZIP_DEFLATED) as zipf: + with zipfile2.ZipFile(TESTFN2, 'a', zipfile.ZIP_DEFLATED) as zipf: with zipf.open('ones', 'r') as r1: data1 = r1.read(500) with zipf.open('twos', 'w') as w1: w1.write(self.data2) data1 += r1.read() self.assertEqual(data1, self.data1) - with zipfile.ZipFile(TESTFN2) as zipf: + with zipfile2.ZipFile(TESTFN2) as zipf: self.assertEqual(zipf.read('twos'), self.data2) def tearDown(self): @@ -2733,7 +2735,7 @@ def setUp(self): os.mkdir(TESTFN2) def test_extract_dir(self): - with zipfile.ZipFile(findfile("zipdir.zip")) as zipf: + with zipfile2.ZipFile(findfile("zipdir.zip")) as zipf: zipf.extractall(TESTFN2) self.assertTrue(os.path.isdir(os.path.join(TESTFN2, "a"))) self.assertTrue(os.path.isdir(os.path.join(TESTFN2, "a", "b"))) @@ -2746,7 +2748,7 @@ def test_bug_6050(self): def test_extract_dir_backslash(self): zfname = findfile("zipdir_backslash.zip") - with zipfile.ZipFile(zfname) as zipf: + with zipfile2.ZipFile(zfname) as zipf: zipf.extractall(TESTFN2) if os.name == 'nt': self.assertTrue(os.path.isdir(os.path.join(TESTFN2, "a"))) @@ -2764,7 +2766,7 @@ def test_write_dir(self): dirpath = os.path.join(TESTFN2, "x") os.mkdir(dirpath) mode = os.stat(dirpath).st_mode & 0xFFFF - with zipfile.ZipFile(TESTFN, "w") as zipf: + with zipfile2.ZipFile(TESTFN, "w") as zipf: zipf.write(dirpath) zinfo = zipf.filelist[0] self.assertTrue(zinfo.filename.endswith("/x/")) @@ -2773,7 +2775,7 @@ def test_write_dir(self): zinfo = zipf.filelist[1] self.assertTrue(zinfo.filename, "y/") self.assertEqual(zinfo.external_attr, (mode << 16) | 0x10) - with zipfile.ZipFile(TESTFN, "r") as zipf: + with zipfile2.ZipFile(TESTFN, "r") as zipf: zinfo = zipf.filelist[0] self.assertTrue(zinfo.filename.endswith("/x/")) self.assertEqual(zinfo.external_attr, (mode << 16) | 0x10) @@ -2788,12 +2790,12 @@ def test_write_dir(self): def test_writestr_dir(self): os.mkdir(os.path.join(TESTFN2, "x")) - with zipfile.ZipFile(TESTFN, "w") as zipf: + with zipfile2.ZipFile(TESTFN, "w") as zipf: zipf.writestr("x/", b'') zinfo = zipf.filelist[0] self.assertEqual(zinfo.filename, "x/") self.assertEqual(zinfo.external_attr, (0o40775 << 16) | 0x10) - with zipfile.ZipFile(TESTFN, "r") as zipf: + with zipfile2.ZipFile(TESTFN, "r") as zipf: zinfo = zipf.filelist[0] self.assertTrue(zinfo.filename.endswith("x/")) self.assertEqual(zinfo.external_attr, (0o40775 << 16) | 0x10) @@ -2804,7 +2806,7 @@ def test_writestr_dir(self): self.assertEqual(os.listdir(target), ["x"]) def test_mkdir(self): - with zipfile.ZipFile(TESTFN, "w") as zf: + with zipfile2.ZipFile(TESTFN, "w") as zf: zf.mkdir("directory") zinfo = zf.filelist[0] self.assertEqual(zinfo.filename, "directory/") @@ -2836,7 +2838,7 @@ def test_mkdir(self): self.assertEqual(set(os.listdir(target)), {"directory", "directory2", "directory3", "directory4"}) def test_create_directory_with_write(self): - with zipfile.ZipFile(TESTFN, "w") as zf: + with zipfile2.ZipFile(TESTFN, "w") as zf: zf.writestr(zipfile.ZipInfo('directory/'), '') zinfo = zf.filelist[0] @@ -2862,7 +2864,7 @@ def test_root_folder_in_zipfile(self): the zip file, this is a strange behavior, but we should support it. """ in_memory_file = io.BytesIO() - zf = zipfile.ZipFile(in_memory_file, "w") + zf = zipfile2.ZipFile(in_memory_file, "w") zf.mkdir('/') zf.writestr('./a.txt', 'aaa') zf.extractall(TESTFN2) @@ -2940,7 +2942,7 @@ def test_test_command(self): def test_list_command(self): zip_name = findfile('zipdir.zip') t = io.StringIO() - with zipfile.ZipFile(zip_name, 'r') as tf: + with zipfile2.ZipFile(zip_name, 'r') as tf: tf.printdir(t) expected = t.getvalue().encode('ascii', 'backslashreplace') for opt in '-l', '--list': @@ -2963,7 +2965,7 @@ def test_create_command(self): try: out = self.zipfilecmd(opt, TESTFN2, *files) self.assertEqual(out, b'') - with zipfile.ZipFile(TESTFN2) as zf: + with zipfile2.ZipFile(TESTFN2) as zf: self.assertEqual(zf.namelist(), namelist) self.assertEqual(zf.read(namelist[0]), b'test 1') self.assertEqual(zf.read(namelist[2]), b'test 2') @@ -2976,7 +2978,7 @@ def test_extract_command(self): with temp_dir() as extdir: out = self.zipfilecmd(opt, zip_name, extdir) self.assertEqual(out, b'') - with zipfile.ZipFile(zip_name) as zf: + with zipfile2.ZipFile(zip_name) as zf: for zi in zf.infolist(): path = os.path.join(extdir, zi.filename.replace('/', os.sep)) @@ -3000,7 +3002,7 @@ def _test_zip_works(self, name): self.assertTrue(zipfile.is_zipfile(name), f'is_zipfile failed on {name}') # Ensure we can operate on these via ZipFile. - with zipfile.ZipFile(name) as zipfp: + with zipfile2.ZipFile(name) as zipfp: for n in zipfp.namelist(): data = zipfp.read(n) self.assertIn(b'FAVORITE_NUMBER', data) @@ -3046,7 +3048,7 @@ def setUp(self): # not otherwise contained in the zip file. # Data elements are encoded bytes (ascii, utf-8, shift_jis). placeholders = ["n1", "n2"] + self.file_names[2:] - with zipfile.ZipFile(TESTFN, mode="w") as tf: + with zipfile2.ZipFile(TESTFN, mode="w") as tf: for temp, content in zip(placeholders, self.file_content): tf.writestr(temp, content, zipfile.ZIP_STORED) # Hack in the Shift JIS names with flag bit 11 (UTF-8) unset. @@ -3077,30 +3079,30 @@ def _test_read(self, zipfp, expected_names, expected_content): def test_read_with_metadata_encoding(self): # Read the ZIP archive with correct metadata_encoding - with zipfile.ZipFile(TESTFN, "r", metadata_encoding='shift_jis') as zipfp: + with zipfile2.ZipFile(TESTFN, "r", metadata_encoding='shift_jis') as zipfp: self._test_read(zipfp, self.file_names, self.file_content) def test_read_without_metadata_encoding(self): # Read the ZIP archive without metadata_encoding expected_names = [name.encode('shift_jis').decode('cp437') for name in self.file_names[:2]] + self.file_names[2:] - with zipfile.ZipFile(TESTFN, "r") as zipfp: + with zipfile2.ZipFile(TESTFN, "r") as zipfp: self._test_read(zipfp, expected_names, self.file_content) def test_read_with_incorrect_metadata_encoding(self): # Read the ZIP archive with incorrect metadata_encoding expected_names = [name.encode('shift_jis').decode('koi8-u') for name in self.file_names[:2]] + self.file_names[2:] - with zipfile.ZipFile(TESTFN, "r", metadata_encoding='koi8-u') as zipfp: + with zipfile2.ZipFile(TESTFN, "r", metadata_encoding='koi8-u') as zipfp: self._test_read(zipfp, expected_names, self.file_content) def test_read_with_unsuitable_metadata_encoding(self): # Read the ZIP archive with metadata_encoding unsuitable for # decoding metadata with self.assertRaises(UnicodeDecodeError): - zipfile.ZipFile(TESTFN, "r", metadata_encoding='ascii') + zipfile2.ZipFile(TESTFN, "r", metadata_encoding='ascii') with self.assertRaises(UnicodeDecodeError): - zipfile.ZipFile(TESTFN, "r", metadata_encoding='utf-8') + zipfile2.ZipFile(TESTFN, "r", metadata_encoding='utf-8') def test_read_after_append(self): newname = '\u56db' # Han 'four' @@ -3109,14 +3111,14 @@ def test_read_after_append(self): expected_names.append(newname) expected_content = (*self.file_content, b"newcontent") - with zipfile.ZipFile(TESTFN, "a") as zipfp: + with zipfile2.ZipFile(TESTFN, "a") as zipfp: zipfp.writestr(newname, "newcontent") self.assertEqual(sorted(zipfp.namelist()), sorted(expected_names)) - with zipfile.ZipFile(TESTFN, "r") as zipfp: + with zipfile2.ZipFile(TESTFN, "r") as zipfp: self._test_read(zipfp, expected_names, expected_content) - with zipfile.ZipFile(TESTFN, "r", metadata_encoding='shift_jis') as zipfp: + with zipfile2.ZipFile(TESTFN, "r", metadata_encoding='shift_jis') as zipfp: self.assertEqual(sorted(zipfp.namelist()), sorted(expected_names)) for i, (name, content) in enumerate(zip(expected_names, expected_content)): info = zipfp.getinfo(name) @@ -3129,7 +3131,7 @@ def test_read_after_append(self): self.assertEqual(zipfp.read(name), content) def test_write_with_metadata_encoding(self): - ZF = zipfile.ZipFile + ZF = zipfile2.ZipFile for mode in ("w", "x", "a"): with self.assertRaisesRegex(ValueError, "^metadata_encoding is only"): From 532ba77a980be3389a51d5750c74d40ca6bf704c Mon Sep 17 00:00:00 2001 From: Ioannis Tziakos Date: Sun, 29 Sep 2024 14:47:51 +0100 Subject: [PATCH 04/41] Ignore python compiled cache --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 3823f17..165a842 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ build .tox zipfile2/_version.py zipfile2*egg-info +__pycache__ \ No newline at end of file From 660eb3f39ecf3aa2b4e448af6659592b09963add Mon Sep 17 00:00:00 2001 From: Ioannis Tziakos Date: Sun, 29 Sep 2024 15:06:06 +0100 Subject: [PATCH 05/41] Move python 3.12 tests to a separate folder --- {cpython-tests => cpython-test}/README.rst | 2 ++ .../cp312}/test_zipfile/__init__.py | 0 .../cp312}/test_zipfile/__main__.py | 0 .../cp312}/test_zipfile/_path/__init__.py | 0 .../cp312}/test_zipfile/_path/_functools.py | 0 .../cp312}/test_zipfile/_path/_itertools.py | 0 .../cp312}/test_zipfile/_path/_support.py | 0 .../cp312}/test_zipfile/_path/_test_params.py | 0 .../cp312}/test_zipfile/_path/test_complexity.py | 0 .../cp312}/test_zipfile/_path/test_path.py | 0 .../cp312}/test_zipfile/_path/write-alpharep.py | 0 .../cp312}/test_zipfile/test_core.py | 4 ++-- .../cp312}/test_zipfile64.py | 0 .../cp312}/test_zipimport.py | 0 .../cp312}/test_zipimport_support.py | 0 .../cp312}/zip_cp437_header.zip | Bin {cpython-tests => cpython-test/cp312}/zipdir.zip | Bin .../cp312}/zipdir_backslash.zip | Bin .../cp312}/ziptestdata/README.md | 0 .../cp312}/ziptestdata/exe_with_z64 | Bin .../cp312}/ziptestdata/exe_with_zip | Bin .../cp312}/ziptestdata/header.sh | 0 .../ziptestdata/testdata_module_inside_zip.py | 0 23 files changed, 4 insertions(+), 2 deletions(-) rename {cpython-tests => cpython-test}/README.rst (57%) rename {cpython-tests => cpython-test/cp312}/test_zipfile/__init__.py (100%) rename {cpython-tests => cpython-test/cp312}/test_zipfile/__main__.py (100%) rename {cpython-tests => cpython-test/cp312}/test_zipfile/_path/__init__.py (100%) rename {cpython-tests => cpython-test/cp312}/test_zipfile/_path/_functools.py (100%) rename {cpython-tests => cpython-test/cp312}/test_zipfile/_path/_itertools.py (100%) rename {cpython-tests => cpython-test/cp312}/test_zipfile/_path/_support.py (100%) rename {cpython-tests => cpython-test/cp312}/test_zipfile/_path/_test_params.py (100%) rename {cpython-tests => cpython-test/cp312}/test_zipfile/_path/test_complexity.py (100%) rename {cpython-tests => cpython-test/cp312}/test_zipfile/_path/test_path.py (100%) rename {cpython-tests => cpython-test/cp312}/test_zipfile/_path/write-alpharep.py (100%) rename {cpython-tests => cpython-test/cp312}/test_zipfile/test_core.py (99%) rename {cpython-tests => cpython-test/cp312}/test_zipfile64.py (100%) rename {cpython-tests => cpython-test/cp312}/test_zipimport.py (100%) rename {cpython-tests => cpython-test/cp312}/test_zipimport_support.py (100%) rename {cpython-tests => cpython-test/cp312}/zip_cp437_header.zip (100%) rename {cpython-tests => cpython-test/cp312}/zipdir.zip (100%) rename {cpython-tests => cpython-test/cp312}/zipdir_backslash.zip (100%) rename {cpython-tests => cpython-test/cp312}/ziptestdata/README.md (100%) rename {cpython-tests => cpython-test/cp312}/ziptestdata/exe_with_z64 (100%) rename {cpython-tests => cpython-test/cp312}/ziptestdata/exe_with_zip (100%) rename {cpython-tests => cpython-test/cp312}/ziptestdata/header.sh (100%) rename {cpython-tests => cpython-test/cp312}/ziptestdata/testdata_module_inside_zip.py (100%) diff --git a/cpython-tests/README.rst b/cpython-test/README.rst similarity index 57% rename from cpython-tests/README.rst rename to cpython-test/README.rst index 1e42df1..1113129 100644 --- a/cpython-tests/README.rst +++ b/cpython-test/README.rst @@ -1,2 +1,4 @@ Zipfile test code copied from the cpython source code. Adapted to test against the zipfile2. +Each folder corresponds to the tests from a specific +python version. diff --git a/cpython-tests/test_zipfile/__init__.py b/cpython-test/cp312/test_zipfile/__init__.py similarity index 100% rename from cpython-tests/test_zipfile/__init__.py rename to cpython-test/cp312/test_zipfile/__init__.py diff --git a/cpython-tests/test_zipfile/__main__.py b/cpython-test/cp312/test_zipfile/__main__.py similarity index 100% rename from cpython-tests/test_zipfile/__main__.py rename to cpython-test/cp312/test_zipfile/__main__.py diff --git a/cpython-tests/test_zipfile/_path/__init__.py b/cpython-test/cp312/test_zipfile/_path/__init__.py similarity index 100% rename from cpython-tests/test_zipfile/_path/__init__.py rename to cpython-test/cp312/test_zipfile/_path/__init__.py diff --git a/cpython-tests/test_zipfile/_path/_functools.py b/cpython-test/cp312/test_zipfile/_path/_functools.py similarity index 100% rename from cpython-tests/test_zipfile/_path/_functools.py rename to cpython-test/cp312/test_zipfile/_path/_functools.py diff --git a/cpython-tests/test_zipfile/_path/_itertools.py b/cpython-test/cp312/test_zipfile/_path/_itertools.py similarity index 100% rename from cpython-tests/test_zipfile/_path/_itertools.py rename to cpython-test/cp312/test_zipfile/_path/_itertools.py diff --git a/cpython-tests/test_zipfile/_path/_support.py b/cpython-test/cp312/test_zipfile/_path/_support.py similarity index 100% rename from cpython-tests/test_zipfile/_path/_support.py rename to cpython-test/cp312/test_zipfile/_path/_support.py diff --git a/cpython-tests/test_zipfile/_path/_test_params.py b/cpython-test/cp312/test_zipfile/_path/_test_params.py similarity index 100% rename from cpython-tests/test_zipfile/_path/_test_params.py rename to cpython-test/cp312/test_zipfile/_path/_test_params.py diff --git a/cpython-tests/test_zipfile/_path/test_complexity.py b/cpython-test/cp312/test_zipfile/_path/test_complexity.py similarity index 100% rename from cpython-tests/test_zipfile/_path/test_complexity.py rename to cpython-test/cp312/test_zipfile/_path/test_complexity.py diff --git a/cpython-tests/test_zipfile/_path/test_path.py b/cpython-test/cp312/test_zipfile/_path/test_path.py similarity index 100% rename from cpython-tests/test_zipfile/_path/test_path.py rename to cpython-test/cp312/test_zipfile/_path/test_path.py diff --git a/cpython-tests/test_zipfile/_path/write-alpharep.py b/cpython-test/cp312/test_zipfile/_path/write-alpharep.py similarity index 100% rename from cpython-tests/test_zipfile/_path/write-alpharep.py rename to cpython-test/cp312/test_zipfile/_path/write-alpharep.py diff --git a/cpython-tests/test_zipfile/test_core.py b/cpython-test/cp312/test_zipfile/test_core.py similarity index 99% rename from cpython-tests/test_zipfile/test_core.py rename to cpython-test/cp312/test_zipfile/test_core.py index 56da079..b094c1d 100644 --- a/cpython-tests/test_zipfile/test_core.py +++ b/cpython-test/cp312/test_zipfile/test_core.py @@ -1561,13 +1561,13 @@ def extractall(self, ar): class OtherTests(unittest.TestCase): def test_open_via_zip_info(self): # Create the ZIP archive - with zipfile2.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED) as zipfp: + with zipfile2.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED, low_level=True) as zipfp: zipfp.writestr("name", "foo") with self.assertWarns(UserWarning): zipfp.writestr("name", "bar") self.assertEqual(zipfp.namelist(), ["name"] * 2) - with zipfile2.ZipFile(TESTFN2, "r") as zipfp: + with zipfile2.ZipFile(TESTFN2, "r", low_level=True) as zipfp: infos = zipfp.infolist() data = b"" for info in infos: diff --git a/cpython-tests/test_zipfile64.py b/cpython-test/cp312/test_zipfile64.py similarity index 100% rename from cpython-tests/test_zipfile64.py rename to cpython-test/cp312/test_zipfile64.py diff --git a/cpython-tests/test_zipimport.py b/cpython-test/cp312/test_zipimport.py similarity index 100% rename from cpython-tests/test_zipimport.py rename to cpython-test/cp312/test_zipimport.py diff --git a/cpython-tests/test_zipimport_support.py b/cpython-test/cp312/test_zipimport_support.py similarity index 100% rename from cpython-tests/test_zipimport_support.py rename to cpython-test/cp312/test_zipimport_support.py diff --git a/cpython-tests/zip_cp437_header.zip b/cpython-test/cp312/zip_cp437_header.zip similarity index 100% rename from cpython-tests/zip_cp437_header.zip rename to cpython-test/cp312/zip_cp437_header.zip diff --git a/cpython-tests/zipdir.zip b/cpython-test/cp312/zipdir.zip similarity index 100% rename from cpython-tests/zipdir.zip rename to cpython-test/cp312/zipdir.zip diff --git a/cpython-tests/zipdir_backslash.zip b/cpython-test/cp312/zipdir_backslash.zip similarity index 100% rename from cpython-tests/zipdir_backslash.zip rename to cpython-test/cp312/zipdir_backslash.zip diff --git a/cpython-tests/ziptestdata/README.md b/cpython-test/cp312/ziptestdata/README.md similarity index 100% rename from cpython-tests/ziptestdata/README.md rename to cpython-test/cp312/ziptestdata/README.md diff --git a/cpython-tests/ziptestdata/exe_with_z64 b/cpython-test/cp312/ziptestdata/exe_with_z64 similarity index 100% rename from cpython-tests/ziptestdata/exe_with_z64 rename to cpython-test/cp312/ziptestdata/exe_with_z64 diff --git a/cpython-tests/ziptestdata/exe_with_zip b/cpython-test/cp312/ziptestdata/exe_with_zip similarity index 100% rename from cpython-tests/ziptestdata/exe_with_zip rename to cpython-test/cp312/ziptestdata/exe_with_zip diff --git a/cpython-tests/ziptestdata/header.sh b/cpython-test/cp312/ziptestdata/header.sh similarity index 100% rename from cpython-tests/ziptestdata/header.sh rename to cpython-test/cp312/ziptestdata/header.sh diff --git a/cpython-tests/ziptestdata/testdata_module_inside_zip.py b/cpython-test/cp312/ziptestdata/testdata_module_inside_zip.py similarity index 100% rename from cpython-tests/ziptestdata/testdata_module_inside_zip.py rename to cpython-test/cp312/ziptestdata/testdata_module_inside_zip.py From 45504c133f008b77ae1dd7d64c0fa4b97bf6efee Mon Sep 17 00:00:00 2001 From: Ioannis Tziakos Date: Sun, 29 Sep 2024 15:17:25 +0100 Subject: [PATCH 06/41] Copy tests for all supported Python versions --- {cpython-test => cpython-tests}/README.rst | 0 cpython-tests/cp310/test_zipfile.py | 3425 +++++++++++++++++ cpython-tests/cp310/test_zipfile64.py | 147 + cpython-tests/cp310/test_zipimport.py | 867 +++++ cpython-tests/cp310/test_zipimport_support.py | 243 ++ .../cp310}/zip_cp437_header.zip | Bin .../cp312 => cpython-tests/cp310}/zipdir.zip | Bin cpython-tests/cp310/ziptestdata/README.md | 35 + .../cp310}/ziptestdata/exe_with_z64 | Bin .../cp310}/ziptestdata/exe_with_zip | Bin .../cp310}/ziptestdata/header.sh | 0 .../ziptestdata/testdata_module_inside_zip.py | 0 cpython-tests/cp311/test_zipfile.py | 3425 +++++++++++++++++ cpython-tests/cp311/test_zipfile64.py | 147 + cpython-tests/cp311/test_zipimport.py | 874 +++++ cpython-tests/cp311/test_zipimport_support.py | 243 ++ cpython-tests/cp311/zip_cp437_header.zip | Bin 0 -> 270 bytes cpython-tests/cp311/zipdir.zip | Bin 0 -> 374 bytes cpython-tests/cp311/ziptestdata/README.md | 35 + cpython-tests/cp311/ziptestdata/exe_with_z64 | Bin 0 -> 978 bytes cpython-tests/cp311/ziptestdata/exe_with_zip | Bin 0 -> 990 bytes cpython-tests/cp311/ziptestdata/header.sh | 24 + .../ziptestdata/testdata_module_inside_zip.py | 2 + .../cp312/test_zipfile/__init__.py | 0 .../cp312/test_zipfile/__main__.py | 0 .../cp312/test_zipfile/_path/__init__.py | 0 .../cp312/test_zipfile/_path/_functools.py | 0 .../cp312/test_zipfile/_path/_itertools.py | 0 .../cp312/test_zipfile/_path/_support.py | 0 .../cp312/test_zipfile/_path/_test_params.py | 0 .../test_zipfile/_path/test_complexity.py | 0 .../cp312/test_zipfile/_path/test_path.py | 0 .../test_zipfile/_path/write-alpharep.py | 0 .../cp312/test_zipfile/test_core.py | 0 .../cp312/test_zipfile64.py | 0 .../cp312/test_zipimport.py | 0 .../cp312/test_zipimport_support.py | 0 cpython-tests/cp312/zip_cp437_header.zip | Bin 0 -> 270 bytes cpython-tests/cp312/zipdir.zip | Bin 0 -> 374 bytes .../cp312/zipdir_backslash.zip | Bin .../cp312/ziptestdata/README.md | 0 cpython-tests/cp312/ziptestdata/exe_with_z64 | Bin 0 -> 978 bytes cpython-tests/cp312/ziptestdata/exe_with_zip | Bin 0 -> 990 bytes cpython-tests/cp312/ziptestdata/header.sh | 24 + .../ziptestdata/testdata_module_inside_zip.py | 2 + cpython-tests/cp38/test_zipfile.py | 3089 +++++++++++++++ cpython-tests/cp38/test_zipfile64.py | 145 + cpython-tests/cp38/test_zipimport.py | 777 ++++ cpython-tests/cp38/test_zipimport_support.py | 242 ++ cpython-tests/cp38/zip_cp437_header.zip | Bin 0 -> 270 bytes cpython-tests/cp38/zipdir.zip | Bin 0 -> 374 bytes cpython-tests/cp38/ziptestdata/README.md | 35 + cpython-tests/cp38/ziptestdata/exe_with_z64 | Bin 0 -> 978 bytes cpython-tests/cp38/ziptestdata/exe_with_zip | Bin 0 -> 990 bytes cpython-tests/cp38/ziptestdata/header.sh | 24 + .../ziptestdata/testdata_module_inside_zip.py | 2 + cpython-tests/cp39/test_zipfile.py | 3136 +++++++++++++++ cpython-tests/cp39/test_zipfile64.py | 145 + cpython-tests/cp39/test_zipimport.py | 774 ++++ cpython-tests/cp39/test_zipimport_support.py | 242 ++ cpython-tests/cp39/zip_cp437_header.zip | Bin 0 -> 270 bytes cpython-tests/cp39/zipdir.zip | Bin 0 -> 374 bytes cpython-tests/cp39/ziptestdata/README.md | 35 + cpython-tests/cp39/ziptestdata/exe_with_z64 | Bin 0 -> 978 bytes cpython-tests/cp39/ziptestdata/exe_with_zip | Bin 0 -> 990 bytes cpython-tests/cp39/ziptestdata/header.sh | 24 + .../ziptestdata/testdata_module_inside_zip.py | 2 + 67 files changed, 18165 insertions(+) rename {cpython-test => cpython-tests}/README.rst (100%) create mode 100644 cpython-tests/cp310/test_zipfile.py create mode 100644 cpython-tests/cp310/test_zipfile64.py create mode 100644 cpython-tests/cp310/test_zipimport.py create mode 100644 cpython-tests/cp310/test_zipimport_support.py rename {cpython-test/cp312 => cpython-tests/cp310}/zip_cp437_header.zip (100%) rename {cpython-test/cp312 => cpython-tests/cp310}/zipdir.zip (100%) create mode 100644 cpython-tests/cp310/ziptestdata/README.md rename {cpython-test/cp312 => cpython-tests/cp310}/ziptestdata/exe_with_z64 (100%) rename {cpython-test/cp312 => cpython-tests/cp310}/ziptestdata/exe_with_zip (100%) rename {cpython-test/cp312 => cpython-tests/cp310}/ziptestdata/header.sh (100%) rename {cpython-test/cp312 => cpython-tests/cp310}/ziptestdata/testdata_module_inside_zip.py (100%) create mode 100644 cpython-tests/cp311/test_zipfile.py create mode 100644 cpython-tests/cp311/test_zipfile64.py create mode 100644 cpython-tests/cp311/test_zipimport.py create mode 100644 cpython-tests/cp311/test_zipimport_support.py create mode 100644 cpython-tests/cp311/zip_cp437_header.zip create mode 100644 cpython-tests/cp311/zipdir.zip create mode 100644 cpython-tests/cp311/ziptestdata/README.md create mode 100644 cpython-tests/cp311/ziptestdata/exe_with_z64 create mode 100644 cpython-tests/cp311/ziptestdata/exe_with_zip create mode 100644 cpython-tests/cp311/ziptestdata/header.sh create mode 100644 cpython-tests/cp311/ziptestdata/testdata_module_inside_zip.py rename {cpython-test => cpython-tests}/cp312/test_zipfile/__init__.py (100%) rename {cpython-test => cpython-tests}/cp312/test_zipfile/__main__.py (100%) rename {cpython-test => cpython-tests}/cp312/test_zipfile/_path/__init__.py (100%) rename {cpython-test => cpython-tests}/cp312/test_zipfile/_path/_functools.py (100%) rename {cpython-test => cpython-tests}/cp312/test_zipfile/_path/_itertools.py (100%) rename {cpython-test => cpython-tests}/cp312/test_zipfile/_path/_support.py (100%) rename {cpython-test => cpython-tests}/cp312/test_zipfile/_path/_test_params.py (100%) rename {cpython-test => cpython-tests}/cp312/test_zipfile/_path/test_complexity.py (100%) rename {cpython-test => cpython-tests}/cp312/test_zipfile/_path/test_path.py (100%) rename {cpython-test => cpython-tests}/cp312/test_zipfile/_path/write-alpharep.py (100%) rename {cpython-test => cpython-tests}/cp312/test_zipfile/test_core.py (100%) rename {cpython-test => cpython-tests}/cp312/test_zipfile64.py (100%) rename {cpython-test => cpython-tests}/cp312/test_zipimport.py (100%) rename {cpython-test => cpython-tests}/cp312/test_zipimport_support.py (100%) create mode 100644 cpython-tests/cp312/zip_cp437_header.zip create mode 100644 cpython-tests/cp312/zipdir.zip rename {cpython-test => cpython-tests}/cp312/zipdir_backslash.zip (100%) rename {cpython-test => cpython-tests}/cp312/ziptestdata/README.md (100%) create mode 100644 cpython-tests/cp312/ziptestdata/exe_with_z64 create mode 100644 cpython-tests/cp312/ziptestdata/exe_with_zip create mode 100644 cpython-tests/cp312/ziptestdata/header.sh create mode 100644 cpython-tests/cp312/ziptestdata/testdata_module_inside_zip.py create mode 100644 cpython-tests/cp38/test_zipfile.py create mode 100644 cpython-tests/cp38/test_zipfile64.py create mode 100644 cpython-tests/cp38/test_zipimport.py create mode 100644 cpython-tests/cp38/test_zipimport_support.py create mode 100644 cpython-tests/cp38/zip_cp437_header.zip create mode 100644 cpython-tests/cp38/zipdir.zip create mode 100644 cpython-tests/cp38/ziptestdata/README.md create mode 100644 cpython-tests/cp38/ziptestdata/exe_with_z64 create mode 100644 cpython-tests/cp38/ziptestdata/exe_with_zip create mode 100644 cpython-tests/cp38/ziptestdata/header.sh create mode 100644 cpython-tests/cp38/ziptestdata/testdata_module_inside_zip.py create mode 100644 cpython-tests/cp39/test_zipfile.py create mode 100644 cpython-tests/cp39/test_zipfile64.py create mode 100644 cpython-tests/cp39/test_zipimport.py create mode 100644 cpython-tests/cp39/test_zipimport_support.py create mode 100644 cpython-tests/cp39/zip_cp437_header.zip create mode 100644 cpython-tests/cp39/zipdir.zip create mode 100644 cpython-tests/cp39/ziptestdata/README.md create mode 100644 cpython-tests/cp39/ziptestdata/exe_with_z64 create mode 100644 cpython-tests/cp39/ziptestdata/exe_with_zip create mode 100644 cpython-tests/cp39/ziptestdata/header.sh create mode 100644 cpython-tests/cp39/ziptestdata/testdata_module_inside_zip.py diff --git a/cpython-test/README.rst b/cpython-tests/README.rst similarity index 100% rename from cpython-test/README.rst rename to cpython-tests/README.rst diff --git a/cpython-tests/cp310/test_zipfile.py b/cpython-tests/cp310/test_zipfile.py new file mode 100644 index 0000000..b4f71a8 --- /dev/null +++ b/cpython-tests/cp310/test_zipfile.py @@ -0,0 +1,3425 @@ +import array +import contextlib +import importlib.util +import io +import itertools +import os +import pathlib +import posixpath +import string +import struct +import subprocess +import sys +from test.support.script_helper import assert_python_ok +import time +import unittest +import unittest.mock as mock +import zipfile +import functools + + +from tempfile import TemporaryFile +from random import randint, random, randbytes + +from test.support import script_helper +from test.support import (findfile, requires_zlib, requires_bz2, + requires_lzma, captured_stdout) +from test.support.os_helper import TESTFN, unlink, rmtree, temp_dir, temp_cwd + + +TESTFN2 = TESTFN + "2" +TESTFNDIR = TESTFN + "d" +FIXEDTEST_SIZE = 1000 +DATAFILES_DIR = 'zipfile_datafiles' + +SMALL_TEST_DATA = [('_ziptest1', '1q2w3e4r5t'), + ('ziptest2dir/_ziptest2', 'qawsedrftg'), + ('ziptest2dir/ziptest3dir/_ziptest3', 'azsxdcfvgb'), + ('ziptest2dir/ziptest3dir/ziptest4dir/_ziptest3', '6y7u8i9o0p')] + +def get_files(test): + yield TESTFN2 + with TemporaryFile() as f: + yield f + test.assertFalse(f.closed) + with io.BytesIO() as f: + yield f + test.assertFalse(f.closed) + +class AbstractTestsWithSourceFile: + @classmethod + def setUpClass(cls): + cls.line_gen = [bytes("Zipfile test line %d. random float: %f\n" % + (i, random()), "ascii") + for i in range(FIXEDTEST_SIZE)] + cls.data = b''.join(cls.line_gen) + + def setUp(self): + # Make a source file with some lines + with open(TESTFN, "wb") as fp: + fp.write(self.data) + + def make_test_archive(self, f, compression, compresslevel=None): + kwargs = {'compression': compression, 'compresslevel': compresslevel} + # Create the ZIP archive + with zipfile.ZipFile(f, "w", **kwargs) as zipfp: + zipfp.write(TESTFN, "another.name") + zipfp.write(TESTFN, TESTFN) + zipfp.writestr("strfile", self.data) + with zipfp.open('written-open-w', mode='w') as f: + for line in self.line_gen: + f.write(line) + + def zip_test(self, f, compression, compresslevel=None): + self.make_test_archive(f, compression, compresslevel) + + # Read the ZIP archive + with zipfile.ZipFile(f, "r", compression) as zipfp: + self.assertEqual(zipfp.read(TESTFN), self.data) + self.assertEqual(zipfp.read("another.name"), self.data) + self.assertEqual(zipfp.read("strfile"), self.data) + + # Print the ZIP directory + fp = io.StringIO() + zipfp.printdir(file=fp) + directory = fp.getvalue() + lines = directory.splitlines() + self.assertEqual(len(lines), 5) # Number of files + header + + self.assertIn('File Name', lines[0]) + self.assertIn('Modified', lines[0]) + self.assertIn('Size', lines[0]) + + fn, date, time_, size = lines[1].split() + self.assertEqual(fn, 'another.name') + self.assertTrue(time.strptime(date, '%Y-%m-%d')) + self.assertTrue(time.strptime(time_, '%H:%M:%S')) + self.assertEqual(size, str(len(self.data))) + + # Check the namelist + names = zipfp.namelist() + self.assertEqual(len(names), 4) + self.assertIn(TESTFN, names) + self.assertIn("another.name", names) + self.assertIn("strfile", names) + self.assertIn("written-open-w", names) + + # Check infolist + infos = zipfp.infolist() + names = [i.filename for i in infos] + self.assertEqual(len(names), 4) + self.assertIn(TESTFN, names) + self.assertIn("another.name", names) + self.assertIn("strfile", names) + self.assertIn("written-open-w", names) + for i in infos: + self.assertEqual(i.file_size, len(self.data)) + + # check getinfo + for nm in (TESTFN, "another.name", "strfile", "written-open-w"): + info = zipfp.getinfo(nm) + self.assertEqual(info.filename, nm) + self.assertEqual(info.file_size, len(self.data)) + + # Check that testzip doesn't raise an exception + zipfp.testzip() + + def test_basic(self): + for f in get_files(self): + self.zip_test(f, self.compression) + + def zip_open_test(self, f, compression): + self.make_test_archive(f, compression) + + # Read the ZIP archive + with zipfile.ZipFile(f, "r", compression) as zipfp: + zipdata1 = [] + with zipfp.open(TESTFN) as zipopen1: + while True: + read_data = zipopen1.read(256) + if not read_data: + break + zipdata1.append(read_data) + + zipdata2 = [] + with zipfp.open("another.name") as zipopen2: + while True: + read_data = zipopen2.read(256) + if not read_data: + break + zipdata2.append(read_data) + + self.assertEqual(b''.join(zipdata1), self.data) + self.assertEqual(b''.join(zipdata2), self.data) + + def test_open(self): + for f in get_files(self): + self.zip_open_test(f, self.compression) + + def test_open_with_pathlike(self): + path = pathlib.Path(TESTFN2) + self.zip_open_test(path, self.compression) + with zipfile.ZipFile(path, "r", self.compression) as zipfp: + self.assertIsInstance(zipfp.filename, str) + + def zip_random_open_test(self, f, compression): + self.make_test_archive(f, compression) + + # Read the ZIP archive + with zipfile.ZipFile(f, "r", compression) as zipfp: + zipdata1 = [] + with zipfp.open(TESTFN) as zipopen1: + while True: + read_data = zipopen1.read(randint(1, 1024)) + if not read_data: + break + zipdata1.append(read_data) + + self.assertEqual(b''.join(zipdata1), self.data) + + def test_random_open(self): + for f in get_files(self): + self.zip_random_open_test(f, self.compression) + + def zip_read1_test(self, f, compression): + self.make_test_archive(f, compression) + + # Read the ZIP archive + with zipfile.ZipFile(f, "r") as zipfp, \ + zipfp.open(TESTFN) as zipopen: + zipdata = [] + while True: + read_data = zipopen.read1(-1) + if not read_data: + break + zipdata.append(read_data) + + self.assertEqual(b''.join(zipdata), self.data) + + def test_read1(self): + for f in get_files(self): + self.zip_read1_test(f, self.compression) + + def zip_read1_10_test(self, f, compression): + self.make_test_archive(f, compression) + + # Read the ZIP archive + with zipfile.ZipFile(f, "r") as zipfp, \ + zipfp.open(TESTFN) as zipopen: + zipdata = [] + while True: + read_data = zipopen.read1(10) + self.assertLessEqual(len(read_data), 10) + if not read_data: + break + zipdata.append(read_data) + + self.assertEqual(b''.join(zipdata), self.data) + + def test_read1_10(self): + for f in get_files(self): + self.zip_read1_10_test(f, self.compression) + + def zip_readline_read_test(self, f, compression): + self.make_test_archive(f, compression) + + # Read the ZIP archive + with zipfile.ZipFile(f, "r") as zipfp, \ + zipfp.open(TESTFN) as zipopen: + data = b'' + while True: + read = zipopen.readline() + if not read: + break + data += read + + read = zipopen.read(100) + if not read: + break + data += read + + self.assertEqual(data, self.data) + + def test_readline_read(self): + # Issue #7610: calls to readline() interleaved with calls to read(). + for f in get_files(self): + self.zip_readline_read_test(f, self.compression) + + def zip_readline_test(self, f, compression): + self.make_test_archive(f, compression) + + # Read the ZIP archive + with zipfile.ZipFile(f, "r") as zipfp: + with zipfp.open(TESTFN) as zipopen: + for line in self.line_gen: + linedata = zipopen.readline() + self.assertEqual(linedata, line) + + def test_readline(self): + for f in get_files(self): + self.zip_readline_test(f, self.compression) + + def zip_readlines_test(self, f, compression): + self.make_test_archive(f, compression) + + # Read the ZIP archive + with zipfile.ZipFile(f, "r") as zipfp: + with zipfp.open(TESTFN) as zipopen: + ziplines = zipopen.readlines() + for line, zipline in zip(self.line_gen, ziplines): + self.assertEqual(zipline, line) + + def test_readlines(self): + for f in get_files(self): + self.zip_readlines_test(f, self.compression) + + def zip_iterlines_test(self, f, compression): + self.make_test_archive(f, compression) + + # Read the ZIP archive + with zipfile.ZipFile(f, "r") as zipfp: + with zipfp.open(TESTFN) as zipopen: + for line, zipline in zip(self.line_gen, zipopen): + self.assertEqual(zipline, line) + + def test_iterlines(self): + for f in get_files(self): + self.zip_iterlines_test(f, self.compression) + + def test_low_compression(self): + """Check for cases where compressed data is larger than original.""" + # Create the ZIP archive + with zipfile.ZipFile(TESTFN2, "w", self.compression) as zipfp: + zipfp.writestr("strfile", '12') + + # Get an open object for strfile + with zipfile.ZipFile(TESTFN2, "r", self.compression) as zipfp: + with zipfp.open("strfile") as openobj: + self.assertEqual(openobj.read(1), b'1') + self.assertEqual(openobj.read(1), b'2') + + def test_writestr_compression(self): + zipfp = zipfile.ZipFile(TESTFN2, "w") + zipfp.writestr("b.txt", "hello world", compress_type=self.compression) + info = zipfp.getinfo('b.txt') + self.assertEqual(info.compress_type, self.compression) + + def test_writestr_compresslevel(self): + zipfp = zipfile.ZipFile(TESTFN2, "w", compresslevel=1) + zipfp.writestr("a.txt", "hello world", compress_type=self.compression) + zipfp.writestr("b.txt", "hello world", compress_type=self.compression, + compresslevel=2) + + # Compression level follows the constructor. + a_info = zipfp.getinfo('a.txt') + self.assertEqual(a_info.compress_type, self.compression) + self.assertEqual(a_info._compresslevel, 1) + + # Compression level is overridden. + b_info = zipfp.getinfo('b.txt') + self.assertEqual(b_info.compress_type, self.compression) + self.assertEqual(b_info._compresslevel, 2) + + def test_read_return_size(self): + # Issue #9837: ZipExtFile.read() shouldn't return more bytes + # than requested. + for test_size in (1, 4095, 4096, 4097, 16384): + file_size = test_size + 1 + junk = randbytes(file_size) + with zipfile.ZipFile(io.BytesIO(), "w", self.compression) as zipf: + zipf.writestr('foo', junk) + with zipf.open('foo', 'r') as fp: + buf = fp.read(test_size) + self.assertEqual(len(buf), test_size) + + def test_truncated_zipfile(self): + fp = io.BytesIO() + with zipfile.ZipFile(fp, mode='w') as zipf: + zipf.writestr('strfile', self.data, compress_type=self.compression) + end_offset = fp.tell() + zipfiledata = fp.getvalue() + + fp = io.BytesIO(zipfiledata) + with zipfile.ZipFile(fp) as zipf: + with zipf.open('strfile') as zipopen: + fp.truncate(end_offset - 20) + with self.assertRaises(EOFError): + zipopen.read() + + fp = io.BytesIO(zipfiledata) + with zipfile.ZipFile(fp) as zipf: + with zipf.open('strfile') as zipopen: + fp.truncate(end_offset - 20) + with self.assertRaises(EOFError): + while zipopen.read(100): + pass + + fp = io.BytesIO(zipfiledata) + with zipfile.ZipFile(fp) as zipf: + with zipf.open('strfile') as zipopen: + fp.truncate(end_offset - 20) + with self.assertRaises(EOFError): + while zipopen.read1(100): + pass + + def test_repr(self): + fname = 'file.name' + for f in get_files(self): + with zipfile.ZipFile(f, 'w', self.compression) as zipfp: + zipfp.write(TESTFN, fname) + r = repr(zipfp) + self.assertIn("mode='w'", r) + + with zipfile.ZipFile(f, 'r') as zipfp: + r = repr(zipfp) + if isinstance(f, str): + self.assertIn('filename=%r' % f, r) + else: + self.assertIn('file=%r' % f, r) + self.assertIn("mode='r'", r) + r = repr(zipfp.getinfo(fname)) + self.assertIn('filename=%r' % fname, r) + self.assertIn('filemode=', r) + self.assertIn('file_size=', r) + if self.compression != zipfile.ZIP_STORED: + self.assertIn('compress_type=', r) + self.assertIn('compress_size=', r) + with zipfp.open(fname) as zipopen: + r = repr(zipopen) + self.assertIn('name=%r' % fname, r) + self.assertIn("mode='r'", r) + if self.compression != zipfile.ZIP_STORED: + self.assertIn('compress_type=', r) + self.assertIn('[closed]', repr(zipopen)) + self.assertIn('[closed]', repr(zipfp)) + + def test_compresslevel_basic(self): + for f in get_files(self): + self.zip_test(f, self.compression, compresslevel=9) + + def test_per_file_compresslevel(self): + """Check that files within a Zip archive can have different + compression levels.""" + with zipfile.ZipFile(TESTFN2, "w", compresslevel=1) as zipfp: + zipfp.write(TESTFN, 'compress_1') + zipfp.write(TESTFN, 'compress_9', compresslevel=9) + one_info = zipfp.getinfo('compress_1') + nine_info = zipfp.getinfo('compress_9') + self.assertEqual(one_info._compresslevel, 1) + self.assertEqual(nine_info._compresslevel, 9) + + def test_writing_errors(self): + class BrokenFile(io.BytesIO): + def write(self, data): + nonlocal count + if count is not None: + if count == stop: + raise OSError + count += 1 + super().write(data) + + stop = 0 + while True: + testfile = BrokenFile() + count = None + with zipfile.ZipFile(testfile, 'w', self.compression) as zipfp: + with zipfp.open('file1', 'w') as f: + f.write(b'data1') + count = 0 + try: + with zipfp.open('file2', 'w') as f: + f.write(b'data2') + except OSError: + stop += 1 + else: + break + finally: + count = None + with zipfile.ZipFile(io.BytesIO(testfile.getvalue())) as zipfp: + self.assertEqual(zipfp.namelist(), ['file1']) + self.assertEqual(zipfp.read('file1'), b'data1') + + with zipfile.ZipFile(io.BytesIO(testfile.getvalue())) as zipfp: + self.assertEqual(zipfp.namelist(), ['file1', 'file2']) + self.assertEqual(zipfp.read('file1'), b'data1') + self.assertEqual(zipfp.read('file2'), b'data2') + + + def tearDown(self): + unlink(TESTFN) + unlink(TESTFN2) + + +class StoredTestsWithSourceFile(AbstractTestsWithSourceFile, + unittest.TestCase): + compression = zipfile.ZIP_STORED + test_low_compression = None + + def zip_test_writestr_permissions(self, f, compression): + # Make sure that writestr and open(... mode='w') create files with + # mode 0600, when they are passed a name rather than a ZipInfo + # instance. + + self.make_test_archive(f, compression) + with zipfile.ZipFile(f, "r") as zipfp: + zinfo = zipfp.getinfo('strfile') + self.assertEqual(zinfo.external_attr, 0o600 << 16) + + zinfo2 = zipfp.getinfo('written-open-w') + self.assertEqual(zinfo2.external_attr, 0o600 << 16) + + def test_writestr_permissions(self): + for f in get_files(self): + self.zip_test_writestr_permissions(f, zipfile.ZIP_STORED) + + def test_absolute_arcnames(self): + with zipfile.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED) as zipfp: + zipfp.write(TESTFN, "/absolute") + + with zipfile.ZipFile(TESTFN2, "r", zipfile.ZIP_STORED) as zipfp: + self.assertEqual(zipfp.namelist(), ["absolute"]) + + def test_append_to_zip_file(self): + """Test appending to an existing zipfile.""" + with zipfile.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED) as zipfp: + zipfp.write(TESTFN, TESTFN) + + with zipfile.ZipFile(TESTFN2, "a", zipfile.ZIP_STORED) as zipfp: + zipfp.writestr("strfile", self.data) + self.assertEqual(zipfp.namelist(), [TESTFN, "strfile"]) + + def test_append_to_non_zip_file(self): + """Test appending to an existing file that is not a zipfile.""" + # NOTE: this test fails if len(d) < 22 because of the first + # line "fpin.seek(-22, 2)" in _EndRecData + data = b'I am not a ZipFile!'*10 + with open(TESTFN2, 'wb') as f: + f.write(data) + + with zipfile.ZipFile(TESTFN2, "a", zipfile.ZIP_STORED) as zipfp: + zipfp.write(TESTFN, TESTFN) + + with open(TESTFN2, 'rb') as f: + f.seek(len(data)) + with zipfile.ZipFile(f, "r") as zipfp: + self.assertEqual(zipfp.namelist(), [TESTFN]) + self.assertEqual(zipfp.read(TESTFN), self.data) + with open(TESTFN2, 'rb') as f: + self.assertEqual(f.read(len(data)), data) + zipfiledata = f.read() + with io.BytesIO(zipfiledata) as bio, zipfile.ZipFile(bio) as zipfp: + self.assertEqual(zipfp.namelist(), [TESTFN]) + self.assertEqual(zipfp.read(TESTFN), self.data) + + def test_read_concatenated_zip_file(self): + with io.BytesIO() as bio: + with zipfile.ZipFile(bio, 'w', zipfile.ZIP_STORED) as zipfp: + zipfp.write(TESTFN, TESTFN) + zipfiledata = bio.getvalue() + data = b'I am not a ZipFile!'*10 + with open(TESTFN2, 'wb') as f: + f.write(data) + f.write(zipfiledata) + + with zipfile.ZipFile(TESTFN2) as zipfp: + self.assertEqual(zipfp.namelist(), [TESTFN]) + self.assertEqual(zipfp.read(TESTFN), self.data) + + def test_append_to_concatenated_zip_file(self): + with io.BytesIO() as bio: + with zipfile.ZipFile(bio, 'w', zipfile.ZIP_STORED) as zipfp: + zipfp.write(TESTFN, TESTFN) + zipfiledata = bio.getvalue() + data = b'I am not a ZipFile!'*1000000 + with open(TESTFN2, 'wb') as f: + f.write(data) + f.write(zipfiledata) + + with zipfile.ZipFile(TESTFN2, 'a') as zipfp: + self.assertEqual(zipfp.namelist(), [TESTFN]) + zipfp.writestr('strfile', self.data) + + with open(TESTFN2, 'rb') as f: + self.assertEqual(f.read(len(data)), data) + zipfiledata = f.read() + with io.BytesIO(zipfiledata) as bio, zipfile.ZipFile(bio) as zipfp: + self.assertEqual(zipfp.namelist(), [TESTFN, 'strfile']) + self.assertEqual(zipfp.read(TESTFN), self.data) + self.assertEqual(zipfp.read('strfile'), self.data) + + def test_ignores_newline_at_end(self): + with zipfile.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED) as zipfp: + zipfp.write(TESTFN, TESTFN) + with open(TESTFN2, 'a', encoding='utf-8') as f: + f.write("\r\n\00\00\00") + with zipfile.ZipFile(TESTFN2, "r") as zipfp: + self.assertIsInstance(zipfp, zipfile.ZipFile) + + def test_ignores_stuff_appended_past_comments(self): + with zipfile.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED) as zipfp: + zipfp.comment = b"this is a comment" + zipfp.write(TESTFN, TESTFN) + with open(TESTFN2, 'a', encoding='utf-8') as f: + f.write("abcdef\r\n") + with zipfile.ZipFile(TESTFN2, "r") as zipfp: + self.assertIsInstance(zipfp, zipfile.ZipFile) + self.assertEqual(zipfp.comment, b"this is a comment") + + def test_write_default_name(self): + """Check that calling ZipFile.write without arcname specified + produces the expected result.""" + with zipfile.ZipFile(TESTFN2, "w") as zipfp: + zipfp.write(TESTFN) + with open(TESTFN, "rb") as f: + self.assertEqual(zipfp.read(TESTFN), f.read()) + + def test_io_on_closed_zipextfile(self): + fname = "somefile.txt" + with zipfile.ZipFile(TESTFN2, mode="w") as zipfp: + zipfp.writestr(fname, "bogus") + + with zipfile.ZipFile(TESTFN2, mode="r") as zipfp: + with zipfp.open(fname) as fid: + fid.close() + self.assertRaises(ValueError, fid.read) + self.assertRaises(ValueError, fid.seek, 0) + self.assertRaises(ValueError, fid.tell) + self.assertRaises(ValueError, fid.readable) + self.assertRaises(ValueError, fid.seekable) + + def test_write_to_readonly(self): + """Check that trying to call write() on a readonly ZipFile object + raises a ValueError.""" + with zipfile.ZipFile(TESTFN2, mode="w") as zipfp: + zipfp.writestr("somefile.txt", "bogus") + + with zipfile.ZipFile(TESTFN2, mode="r") as zipfp: + self.assertRaises(ValueError, zipfp.write, TESTFN) + + with zipfile.ZipFile(TESTFN2, mode="r") as zipfp: + with self.assertRaises(ValueError): + zipfp.open(TESTFN, mode='w') + + def test_add_file_before_1980(self): + # Set atime and mtime to 1970-01-01 + os.utime(TESTFN, (0, 0)) + with zipfile.ZipFile(TESTFN2, "w") as zipfp: + self.assertRaises(ValueError, zipfp.write, TESTFN) + + with zipfile.ZipFile(TESTFN2, "w", strict_timestamps=False) as zipfp: + zipfp.write(TESTFN) + zinfo = zipfp.getinfo(TESTFN) + self.assertEqual(zinfo.date_time, (1980, 1, 1, 0, 0, 0)) + + def test_add_file_after_2107(self): + # Set atime and mtime to 2108-12-30 + ts = 4386268800 + try: + time.localtime(ts) + except OverflowError: + self.skipTest(f'time.localtime({ts}) raises OverflowError') + try: + os.utime(TESTFN, (ts, ts)) + except OverflowError: + self.skipTest('Host fs cannot set timestamp to required value.') + + mtime_ns = os.stat(TESTFN).st_mtime_ns + if mtime_ns != (4386268800 * 10**9): + # XFS filesystem is limited to 32-bit timestamp, but the syscall + # didn't fail. Moreover, there is a VFS bug which returns + # a cached timestamp which is different than the value on disk. + # + # Test st_mtime_ns rather than st_mtime to avoid rounding issues. + # + # https://bugzilla.redhat.com/show_bug.cgi?id=1795576 + # https://bugs.python.org/issue39460#msg360952 + self.skipTest(f"Linux VFS/XFS kernel bug detected: {mtime_ns=}") + + with zipfile.ZipFile(TESTFN2, "w") as zipfp: + self.assertRaises(struct.error, zipfp.write, TESTFN) + + with zipfile.ZipFile(TESTFN2, "w", strict_timestamps=False) as zipfp: + zipfp.write(TESTFN) + zinfo = zipfp.getinfo(TESTFN) + self.assertEqual(zinfo.date_time, (2107, 12, 31, 23, 59, 59)) + + +@requires_zlib() +class DeflateTestsWithSourceFile(AbstractTestsWithSourceFile, + unittest.TestCase): + compression = zipfile.ZIP_DEFLATED + + def test_per_file_compression(self): + """Check that files within a Zip archive can have different + compression options.""" + with zipfile.ZipFile(TESTFN2, "w") as zipfp: + zipfp.write(TESTFN, 'storeme', zipfile.ZIP_STORED) + zipfp.write(TESTFN, 'deflateme', zipfile.ZIP_DEFLATED) + sinfo = zipfp.getinfo('storeme') + dinfo = zipfp.getinfo('deflateme') + self.assertEqual(sinfo.compress_type, zipfile.ZIP_STORED) + self.assertEqual(dinfo.compress_type, zipfile.ZIP_DEFLATED) + +@requires_bz2() +class Bzip2TestsWithSourceFile(AbstractTestsWithSourceFile, + unittest.TestCase): + compression = zipfile.ZIP_BZIP2 + +@requires_lzma() +class LzmaTestsWithSourceFile(AbstractTestsWithSourceFile, + unittest.TestCase): + compression = zipfile.ZIP_LZMA + + +class AbstractTestZip64InSmallFiles: + # These tests test the ZIP64 functionality without using large files, + # see test_zipfile64 for proper tests. + + @classmethod + def setUpClass(cls): + line_gen = (bytes("Test of zipfile line %d." % i, "ascii") + for i in range(0, FIXEDTEST_SIZE)) + cls.data = b'\n'.join(line_gen) + + def setUp(self): + self._limit = zipfile.ZIP64_LIMIT + self._filecount_limit = zipfile.ZIP_FILECOUNT_LIMIT + zipfile.ZIP64_LIMIT = 1000 + zipfile.ZIP_FILECOUNT_LIMIT = 9 + + # Make a source file with some lines + with open(TESTFN, "wb") as fp: + fp.write(self.data) + + def zip_test(self, f, compression): + # Create the ZIP archive + with zipfile.ZipFile(f, "w", compression, allowZip64=True) as zipfp: + zipfp.write(TESTFN, "another.name") + zipfp.write(TESTFN, TESTFN) + zipfp.writestr("strfile", self.data) + + # Read the ZIP archive + with zipfile.ZipFile(f, "r", compression) as zipfp: + self.assertEqual(zipfp.read(TESTFN), self.data) + self.assertEqual(zipfp.read("another.name"), self.data) + self.assertEqual(zipfp.read("strfile"), self.data) + + # Print the ZIP directory + fp = io.StringIO() + zipfp.printdir(fp) + + directory = fp.getvalue() + lines = directory.splitlines() + self.assertEqual(len(lines), 4) # Number of files + header + + self.assertIn('File Name', lines[0]) + self.assertIn('Modified', lines[0]) + self.assertIn('Size', lines[0]) + + fn, date, time_, size = lines[1].split() + self.assertEqual(fn, 'another.name') + self.assertTrue(time.strptime(date, '%Y-%m-%d')) + self.assertTrue(time.strptime(time_, '%H:%M:%S')) + self.assertEqual(size, str(len(self.data))) + + # Check the namelist + names = zipfp.namelist() + self.assertEqual(len(names), 3) + self.assertIn(TESTFN, names) + self.assertIn("another.name", names) + self.assertIn("strfile", names) + + # Check infolist + infos = zipfp.infolist() + names = [i.filename for i in infos] + self.assertEqual(len(names), 3) + self.assertIn(TESTFN, names) + self.assertIn("another.name", names) + self.assertIn("strfile", names) + for i in infos: + self.assertEqual(i.file_size, len(self.data)) + + # check getinfo + for nm in (TESTFN, "another.name", "strfile"): + info = zipfp.getinfo(nm) + self.assertEqual(info.filename, nm) + self.assertEqual(info.file_size, len(self.data)) + + # Check that testzip doesn't raise an exception + zipfp.testzip() + + def test_basic(self): + for f in get_files(self): + self.zip_test(f, self.compression) + + def test_too_many_files(self): + # This test checks that more than 64k files can be added to an archive, + # and that the resulting archive can be read properly by ZipFile + zipf = zipfile.ZipFile(TESTFN, "w", self.compression, + allowZip64=True) + zipf.debug = 100 + numfiles = 15 + for i in range(numfiles): + zipf.writestr("foo%08d" % i, "%d" % (i**3 % 57)) + self.assertEqual(len(zipf.namelist()), numfiles) + zipf.close() + + zipf2 = zipfile.ZipFile(TESTFN, "r", self.compression) + self.assertEqual(len(zipf2.namelist()), numfiles) + for i in range(numfiles): + content = zipf2.read("foo%08d" % i).decode('ascii') + self.assertEqual(content, "%d" % (i**3 % 57)) + zipf2.close() + + def test_too_many_files_append(self): + zipf = zipfile.ZipFile(TESTFN, "w", self.compression, + allowZip64=False) + zipf.debug = 100 + numfiles = 9 + for i in range(numfiles): + zipf.writestr("foo%08d" % i, "%d" % (i**3 % 57)) + self.assertEqual(len(zipf.namelist()), numfiles) + with self.assertRaises(zipfile.LargeZipFile): + zipf.writestr("foo%08d" % numfiles, b'') + self.assertEqual(len(zipf.namelist()), numfiles) + zipf.close() + + zipf = zipfile.ZipFile(TESTFN, "a", self.compression, + allowZip64=False) + zipf.debug = 100 + self.assertEqual(len(zipf.namelist()), numfiles) + with self.assertRaises(zipfile.LargeZipFile): + zipf.writestr("foo%08d" % numfiles, b'') + self.assertEqual(len(zipf.namelist()), numfiles) + zipf.close() + + zipf = zipfile.ZipFile(TESTFN, "a", self.compression, + allowZip64=True) + zipf.debug = 100 + self.assertEqual(len(zipf.namelist()), numfiles) + numfiles2 = 15 + for i in range(numfiles, numfiles2): + zipf.writestr("foo%08d" % i, "%d" % (i**3 % 57)) + self.assertEqual(len(zipf.namelist()), numfiles2) + zipf.close() + + zipf2 = zipfile.ZipFile(TESTFN, "r", self.compression) + self.assertEqual(len(zipf2.namelist()), numfiles2) + for i in range(numfiles2): + content = zipf2.read("foo%08d" % i).decode('ascii') + self.assertEqual(content, "%d" % (i**3 % 57)) + zipf2.close() + + def tearDown(self): + zipfile.ZIP64_LIMIT = self._limit + zipfile.ZIP_FILECOUNT_LIMIT = self._filecount_limit + unlink(TESTFN) + unlink(TESTFN2) + + +class StoredTestZip64InSmallFiles(AbstractTestZip64InSmallFiles, + unittest.TestCase): + compression = zipfile.ZIP_STORED + + def large_file_exception_test(self, f, compression): + with zipfile.ZipFile(f, "w", compression, allowZip64=False) as zipfp: + self.assertRaises(zipfile.LargeZipFile, + zipfp.write, TESTFN, "another.name") + + def large_file_exception_test2(self, f, compression): + with zipfile.ZipFile(f, "w", compression, allowZip64=False) as zipfp: + self.assertRaises(zipfile.LargeZipFile, + zipfp.writestr, "another.name", self.data) + + def test_large_file_exception(self): + for f in get_files(self): + self.large_file_exception_test(f, zipfile.ZIP_STORED) + self.large_file_exception_test2(f, zipfile.ZIP_STORED) + + def test_absolute_arcnames(self): + with zipfile.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED, + allowZip64=True) as zipfp: + zipfp.write(TESTFN, "/absolute") + + with zipfile.ZipFile(TESTFN2, "r", zipfile.ZIP_STORED) as zipfp: + self.assertEqual(zipfp.namelist(), ["absolute"]) + + def test_append(self): + # Test that appending to the Zip64 archive doesn't change + # extra fields of existing entries. + with zipfile.ZipFile(TESTFN2, "w", allowZip64=True) as zipfp: + zipfp.writestr("strfile", self.data) + with zipfile.ZipFile(TESTFN2, "r", allowZip64=True) as zipfp: + zinfo = zipfp.getinfo("strfile") + extra = zinfo.extra + with zipfile.ZipFile(TESTFN2, "a", allowZip64=True) as zipfp: + zipfp.writestr("strfile2", self.data) + with zipfile.ZipFile(TESTFN2, "r", allowZip64=True) as zipfp: + zinfo = zipfp.getinfo("strfile") + self.assertEqual(zinfo.extra, extra) + + def make_zip64_file( + self, file_size_64_set=False, file_size_extra=False, + compress_size_64_set=False, compress_size_extra=False, + header_offset_64_set=False, header_offset_extra=False, + ): + """Generate bytes sequence for a zip with (incomplete) zip64 data. + + The actual values (not the zip 64 0xffffffff values) stored in the file + are: + file_size: 8 + compress_size: 8 + header_offset: 0 + """ + actual_size = 8 + actual_header_offset = 0 + local_zip64_fields = [] + central_zip64_fields = [] + + file_size = actual_size + if file_size_64_set: + file_size = 0xffffffff + if file_size_extra: + local_zip64_fields.append(actual_size) + central_zip64_fields.append(actual_size) + file_size = struct.pack("e|f"g?h*i', ','), r'a\b,c_d_e_f_g_h_i') + self.assertEqual(san('../../foo../../ba..r', '/'), r'foo/ba..r') + + def test_extract_hackers_arcnames_common_cases(self): + common_hacknames = [ + ('../foo/bar', 'foo/bar'), + ('foo/../bar', 'foo/bar'), + ('foo/../../bar', 'foo/bar'), + ('foo/bar/..', 'foo/bar'), + ('./../foo/bar', 'foo/bar'), + ('/foo/bar', 'foo/bar'), + ('/foo/../bar', 'foo/bar'), + ('/foo/../../bar', 'foo/bar'), + ] + self._test_extract_hackers_arcnames(common_hacknames) + + @unittest.skipIf(os.path.sep != '\\', 'Requires \\ as path separator.') + def test_extract_hackers_arcnames_windows_only(self): + """Test combination of path fixing and windows name sanitization.""" + windows_hacknames = [ + (r'..\foo\bar', 'foo/bar'), + (r'..\/foo\/bar', 'foo/bar'), + (r'foo/\..\/bar', 'foo/bar'), + (r'foo\/../\bar', 'foo/bar'), + (r'C:foo/bar', 'foo/bar'), + (r'C:/foo/bar', 'foo/bar'), + (r'C://foo/bar', 'foo/bar'), + (r'C:\foo\bar', 'foo/bar'), + (r'//conky/mountpoint/foo/bar', 'foo/bar'), + (r'\\conky\mountpoint\foo\bar', 'foo/bar'), + (r'///conky/mountpoint/foo/bar', 'conky/mountpoint/foo/bar'), + (r'\\\conky\mountpoint\foo\bar', 'conky/mountpoint/foo/bar'), + (r'//conky//mountpoint/foo/bar', 'conky/mountpoint/foo/bar'), + (r'\\conky\\mountpoint\foo\bar', 'conky/mountpoint/foo/bar'), + (r'//?/C:/foo/bar', 'foo/bar'), + (r'\\?\C:\foo\bar', 'foo/bar'), + (r'C:/../C:/foo/bar', 'C_/foo/bar'), + (r'a:b\ce|f"g?h*i', 'b/c_d_e_f_g_h_i'), + ('../../foo../../ba..r', 'foo/ba..r'), + ] + self._test_extract_hackers_arcnames(windows_hacknames) + + @unittest.skipIf(os.path.sep != '/', r'Requires / as path separator.') + def test_extract_hackers_arcnames_posix_only(self): + posix_hacknames = [ + ('//foo/bar', 'foo/bar'), + ('../../foo../../ba..r', 'foo../ba..r'), + (r'foo/..\bar', r'foo/..\bar'), + ] + self._test_extract_hackers_arcnames(posix_hacknames) + + def _test_extract_hackers_arcnames(self, hacknames): + for arcname, fixedname in hacknames: + content = b'foobar' + arcname.encode() + with zipfile.ZipFile(TESTFN2, 'w', zipfile.ZIP_STORED) as zipfp: + zinfo = zipfile.ZipInfo() + # preserve backslashes + zinfo.filename = arcname + zinfo.external_attr = 0o600 << 16 + zipfp.writestr(zinfo, content) + + arcname = arcname.replace(os.sep, "/") + targetpath = os.path.join('target', 'subdir', 'subsub') + correctfile = os.path.join(targetpath, *fixedname.split('/')) + + with zipfile.ZipFile(TESTFN2, 'r') as zipfp: + writtenfile = zipfp.extract(arcname, targetpath) + self.assertEqual(writtenfile, correctfile, + msg='extract %r: %r != %r' % + (arcname, writtenfile, correctfile)) + self.check_file(correctfile, content) + rmtree('target') + + with zipfile.ZipFile(TESTFN2, 'r') as zipfp: + zipfp.extractall(targetpath) + self.check_file(correctfile, content) + rmtree('target') + + correctfile = os.path.join(os.getcwd(), *fixedname.split('/')) + + with zipfile.ZipFile(TESTFN2, 'r') as zipfp: + writtenfile = zipfp.extract(arcname) + self.assertEqual(writtenfile, correctfile, + msg="extract %r" % arcname) + self.check_file(correctfile, content) + rmtree(fixedname.split('/')[0]) + + with zipfile.ZipFile(TESTFN2, 'r') as zipfp: + zipfp.extractall() + self.check_file(correctfile, content) + rmtree(fixedname.split('/')[0]) + + unlink(TESTFN2) + + +class OtherTests(unittest.TestCase): + def test_open_via_zip_info(self): + # Create the ZIP archive + with zipfile.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED) as zipfp: + zipfp.writestr("name", "foo") + with self.assertWarns(UserWarning): + zipfp.writestr("name", "bar") + self.assertEqual(zipfp.namelist(), ["name"] * 2) + + with zipfile.ZipFile(TESTFN2, "r") as zipfp: + infos = zipfp.infolist() + data = b"" + for info in infos: + with zipfp.open(info) as zipopen: + data += zipopen.read() + self.assertIn(data, {b"foobar", b"barfoo"}) + data = b"" + for info in infos: + data += zipfp.read(info) + self.assertIn(data, {b"foobar", b"barfoo"}) + + def test_writestr_extended_local_header_issue1202(self): + with zipfile.ZipFile(TESTFN2, 'w') as orig_zip: + for data in 'abcdefghijklmnop': + zinfo = zipfile.ZipInfo(data) + zinfo.flag_bits |= 0x08 # Include an extended local header. + orig_zip.writestr(zinfo, data) + + def test_close(self): + """Check that the zipfile is closed after the 'with' block.""" + with zipfile.ZipFile(TESTFN2, "w") as zipfp: + for fpath, fdata in SMALL_TEST_DATA: + zipfp.writestr(fpath, fdata) + self.assertIsNotNone(zipfp.fp, 'zipfp is not open') + self.assertIsNone(zipfp.fp, 'zipfp is not closed') + + with zipfile.ZipFile(TESTFN2, "r") as zipfp: + self.assertIsNotNone(zipfp.fp, 'zipfp is not open') + self.assertIsNone(zipfp.fp, 'zipfp is not closed') + + def test_close_on_exception(self): + """Check that the zipfile is closed if an exception is raised in the + 'with' block.""" + with zipfile.ZipFile(TESTFN2, "w") as zipfp: + for fpath, fdata in SMALL_TEST_DATA: + zipfp.writestr(fpath, fdata) + + try: + with zipfile.ZipFile(TESTFN2, "r") as zipfp2: + raise zipfile.BadZipFile() + except zipfile.BadZipFile: + self.assertIsNone(zipfp2.fp, 'zipfp is not closed') + + def test_unsupported_version(self): + # File has an extract_version of 120 + data = (b'PK\x03\x04x\x00\x00\x00\x00\x00!p\xa1@\x00\x00\x00\x00\x00\x00' + b'\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00xPK\x01\x02x\x03x\x00\x00\x00\x00' + b'\x00!p\xa1@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00' + b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x01\x00\x00\x00\x00xPK\x05\x06' + b'\x00\x00\x00\x00\x01\x00\x01\x00/\x00\x00\x00\x1f\x00\x00\x00\x00\x00') + + self.assertRaises(NotImplementedError, zipfile.ZipFile, + io.BytesIO(data), 'r') + + @requires_zlib() + def test_read_unicode_filenames(self): + # bug #10801 + fname = findfile('zip_cp437_header.zip') + with zipfile.ZipFile(fname) as zipfp: + for name in zipfp.namelist(): + zipfp.open(name).close() + + def test_write_unicode_filenames(self): + with zipfile.ZipFile(TESTFN, "w") as zf: + zf.writestr("foo.txt", "Test for unicode filename") + zf.writestr("\xf6.txt", "Test for unicode filename") + self.assertIsInstance(zf.infolist()[0].filename, str) + + with zipfile.ZipFile(TESTFN, "r") as zf: + self.assertEqual(zf.filelist[0].filename, "foo.txt") + self.assertEqual(zf.filelist[1].filename, "\xf6.txt") + + def test_read_after_write_unicode_filenames(self): + with zipfile.ZipFile(TESTFN2, 'w') as zipfp: + zipfp.writestr('приклад', b'sample') + self.assertEqual(zipfp.read('приклад'), b'sample') + + def test_exclusive_create_zip_file(self): + """Test exclusive creating a new zipfile.""" + unlink(TESTFN2) + filename = 'testfile.txt' + content = b'hello, world. this is some content.' + with zipfile.ZipFile(TESTFN2, "x", zipfile.ZIP_STORED) as zipfp: + zipfp.writestr(filename, content) + with self.assertRaises(FileExistsError): + zipfile.ZipFile(TESTFN2, "x", zipfile.ZIP_STORED) + with zipfile.ZipFile(TESTFN2, "r") as zipfp: + self.assertEqual(zipfp.namelist(), [filename]) + self.assertEqual(zipfp.read(filename), content) + + def test_create_non_existent_file_for_append(self): + if os.path.exists(TESTFN): + os.unlink(TESTFN) + + filename = 'testfile.txt' + content = b'hello, world. this is some content.' + + try: + with zipfile.ZipFile(TESTFN, 'a') as zf: + zf.writestr(filename, content) + except OSError: + self.fail('Could not append data to a non-existent zip file.') + + self.assertTrue(os.path.exists(TESTFN)) + + with zipfile.ZipFile(TESTFN, 'r') as zf: + self.assertEqual(zf.read(filename), content) + + def test_close_erroneous_file(self): + # This test checks that the ZipFile constructor closes the file object + # it opens if there's an error in the file. If it doesn't, the + # traceback holds a reference to the ZipFile object and, indirectly, + # the file object. + # On Windows, this causes the os.unlink() call to fail because the + # underlying file is still open. This is SF bug #412214. + # + with open(TESTFN, "w", encoding="utf-8") as fp: + fp.write("this is not a legal zip file\n") + try: + zf = zipfile.ZipFile(TESTFN) + except zipfile.BadZipFile: + pass + + def test_is_zip_erroneous_file(self): + """Check that is_zipfile() correctly identifies non-zip files.""" + # - passing a filename + with open(TESTFN, "w", encoding='utf-8') as fp: + fp.write("this is not a legal zip file\n") + self.assertFalse(zipfile.is_zipfile(TESTFN)) + # - passing a path-like object + self.assertFalse(zipfile.is_zipfile(pathlib.Path(TESTFN))) + # - passing a file object + with open(TESTFN, "rb") as fp: + self.assertFalse(zipfile.is_zipfile(fp)) + # - passing a file-like object + fp = io.BytesIO() + fp.write(b"this is not a legal zip file\n") + self.assertFalse(zipfile.is_zipfile(fp)) + fp.seek(0, 0) + self.assertFalse(zipfile.is_zipfile(fp)) + + def test_damaged_zipfile(self): + """Check that zipfiles with missing bytes at the end raise BadZipFile.""" + # - Create a valid zip file + fp = io.BytesIO() + with zipfile.ZipFile(fp, mode="w") as zipf: + zipf.writestr("foo.txt", b"O, for a Muse of Fire!") + zipfiledata = fp.getvalue() + + # - Now create copies of it missing the last N bytes and make sure + # a BadZipFile exception is raised when we try to open it + for N in range(len(zipfiledata)): + fp = io.BytesIO(zipfiledata[:N]) + self.assertRaises(zipfile.BadZipFile, zipfile.ZipFile, fp) + + def test_is_zip_valid_file(self): + """Check that is_zipfile() correctly identifies zip files.""" + # - passing a filename + with zipfile.ZipFile(TESTFN, mode="w") as zipf: + zipf.writestr("foo.txt", b"O, for a Muse of Fire!") + + self.assertTrue(zipfile.is_zipfile(TESTFN)) + # - passing a file object + with open(TESTFN, "rb") as fp: + self.assertTrue(zipfile.is_zipfile(fp)) + fp.seek(0, 0) + zip_contents = fp.read() + # - passing a file-like object + fp = io.BytesIO() + fp.write(zip_contents) + self.assertTrue(zipfile.is_zipfile(fp)) + fp.seek(0, 0) + self.assertTrue(zipfile.is_zipfile(fp)) + + def test_non_existent_file_raises_OSError(self): + # make sure we don't raise an AttributeError when a partially-constructed + # ZipFile instance is finalized; this tests for regression on SF tracker + # bug #403871. + + # The bug we're testing for caused an AttributeError to be raised + # when a ZipFile instance was created for a file that did not + # exist; the .fp member was not initialized but was needed by the + # __del__() method. Since the AttributeError is in the __del__(), + # it is ignored, but the user should be sufficiently annoyed by + # the message on the output that regression will be noticed + # quickly. + self.assertRaises(OSError, zipfile.ZipFile, TESTFN) + + def test_empty_file_raises_BadZipFile(self): + f = open(TESTFN, 'w', encoding='utf-8') + f.close() + self.assertRaises(zipfile.BadZipFile, zipfile.ZipFile, TESTFN) + + with open(TESTFN, 'w', encoding='utf-8') as fp: + fp.write("short file") + self.assertRaises(zipfile.BadZipFile, zipfile.ZipFile, TESTFN) + + def test_negative_central_directory_offset_raises_BadZipFile(self): + # Zip file containing an empty EOCD record + buffer = bytearray(b'PK\x05\x06' + b'\0'*18) + + # Set the size of the central directory bytes to become 1, + # causing the central directory offset to become negative + for dirsize in 1, 2**32-1: + buffer[12:16] = struct.pack(' os.path.getsize(TESTFN)) + with zipfile.ZipFile(TESTFN,mode="r") as zipf: + self.assertEqual(zipf.comment, b"shorter comment") + + def test_unicode_comment(self): + with zipfile.ZipFile(TESTFN, "w", zipfile.ZIP_STORED) as zipf: + zipf.writestr("foo.txt", "O, for a Muse of Fire!") + with self.assertRaises(TypeError): + zipf.comment = "this is an error" + + def test_change_comment_in_empty_archive(self): + with zipfile.ZipFile(TESTFN, "a", zipfile.ZIP_STORED) as zipf: + self.assertFalse(zipf.filelist) + zipf.comment = b"this is a comment" + with zipfile.ZipFile(TESTFN, "r") as zipf: + self.assertEqual(zipf.comment, b"this is a comment") + + def test_change_comment_in_nonempty_archive(self): + with zipfile.ZipFile(TESTFN, "w", zipfile.ZIP_STORED) as zipf: + zipf.writestr("foo.txt", "O, for a Muse of Fire!") + with zipfile.ZipFile(TESTFN, "a", zipfile.ZIP_STORED) as zipf: + self.assertTrue(zipf.filelist) + zipf.comment = b"this is a comment" + with zipfile.ZipFile(TESTFN, "r") as zipf: + self.assertEqual(zipf.comment, b"this is a comment") + + def test_empty_zipfile(self): + # Check that creating a file in 'w' or 'a' mode and closing without + # adding any files to the archives creates a valid empty ZIP file + zipf = zipfile.ZipFile(TESTFN, mode="w") + zipf.close() + try: + zipf = zipfile.ZipFile(TESTFN, mode="r") + except zipfile.BadZipFile: + self.fail("Unable to create empty ZIP file in 'w' mode") + + zipf = zipfile.ZipFile(TESTFN, mode="a") + zipf.close() + try: + zipf = zipfile.ZipFile(TESTFN, mode="r") + except: + self.fail("Unable to create empty ZIP file in 'a' mode") + + def test_open_empty_file(self): + # Issue 1710703: Check that opening a file with less than 22 bytes + # raises a BadZipFile exception (rather than the previously unhelpful + # OSError) + f = open(TESTFN, 'w', encoding='utf-8') + f.close() + self.assertRaises(zipfile.BadZipFile, zipfile.ZipFile, TESTFN, 'r') + + def test_create_zipinfo_before_1980(self): + self.assertRaises(ValueError, + zipfile.ZipInfo, 'seventies', (1979, 1, 1, 0, 0, 0)) + + def test_create_empty_zipinfo_repr(self): + """Before bpo-26185, repr() on empty ZipInfo object was failing.""" + zi = zipfile.ZipInfo(filename="empty") + self.assertEqual(repr(zi), "") + + def test_create_empty_zipinfo_default_attributes(self): + """Ensure all required attributes are set.""" + zi = zipfile.ZipInfo() + self.assertEqual(zi.orig_filename, "NoName") + self.assertEqual(zi.filename, "NoName") + self.assertEqual(zi.date_time, (1980, 1, 1, 0, 0, 0)) + self.assertEqual(zi.compress_type, zipfile.ZIP_STORED) + self.assertEqual(zi.comment, b"") + self.assertEqual(zi.extra, b"") + self.assertIn(zi.create_system, (0, 3)) + self.assertEqual(zi.create_version, zipfile.DEFAULT_VERSION) + self.assertEqual(zi.extract_version, zipfile.DEFAULT_VERSION) + self.assertEqual(zi.reserved, 0) + self.assertEqual(zi.flag_bits, 0) + self.assertEqual(zi.volume, 0) + self.assertEqual(zi.internal_attr, 0) + self.assertEqual(zi.external_attr, 0) + + # Before bpo-26185, both were missing + self.assertEqual(zi.file_size, 0) + self.assertEqual(zi.compress_size, 0) + + def test_zipfile_with_short_extra_field(self): + """If an extra field in the header is less than 4 bytes, skip it.""" + zipdata = ( + b'PK\x03\x04\x14\x00\x00\x00\x00\x00\x93\x9b\xad@\x8b\x9e' + b'\xd9\xd3\x01\x00\x00\x00\x01\x00\x00\x00\x03\x00\x03\x00ab' + b'c\x00\x00\x00APK\x01\x02\x14\x03\x14\x00\x00\x00\x00' + b'\x00\x93\x9b\xad@\x8b\x9e\xd9\xd3\x01\x00\x00\x00\x01\x00\x00' + b'\x00\x03\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x00' + b'\x00\x00\x00abc\x00\x00PK\x05\x06\x00\x00\x00\x00' + b'\x01\x00\x01\x003\x00\x00\x00%\x00\x00\x00\x00\x00' + ) + with zipfile.ZipFile(io.BytesIO(zipdata), 'r') as zipf: + # testzip returns the name of the first corrupt file, or None + self.assertIsNone(zipf.testzip()) + + def test_open_conflicting_handles(self): + # It's only possible to open one writable file handle at a time + msg1 = b"It's fun to charter an accountant!" + msg2 = b"And sail the wide accountant sea" + msg3 = b"To find, explore the funds offshore" + with zipfile.ZipFile(TESTFN2, 'w', zipfile.ZIP_STORED) as zipf: + with zipf.open('foo', mode='w') as w2: + w2.write(msg1) + with zipf.open('bar', mode='w') as w1: + with self.assertRaises(ValueError): + zipf.open('handle', mode='w') + with self.assertRaises(ValueError): + zipf.open('foo', mode='r') + with self.assertRaises(ValueError): + zipf.writestr('str', 'abcde') + with self.assertRaises(ValueError): + zipf.write(__file__, 'file') + with self.assertRaises(ValueError): + zipf.close() + w1.write(msg2) + with zipf.open('baz', mode='w') as w2: + w2.write(msg3) + + with zipfile.ZipFile(TESTFN2, 'r') as zipf: + self.assertEqual(zipf.read('foo'), msg1) + self.assertEqual(zipf.read('bar'), msg2) + self.assertEqual(zipf.read('baz'), msg3) + self.assertEqual(zipf.namelist(), ['foo', 'bar', 'baz']) + + def test_seek_tell(self): + # Test seek functionality + txt = b"Where's Bruce?" + bloc = txt.find(b"Bruce") + # Check seek on a file + with zipfile.ZipFile(TESTFN, "w") as zipf: + zipf.writestr("foo.txt", txt) + with zipfile.ZipFile(TESTFN, "r") as zipf: + with zipf.open("foo.txt", "r") as fp: + fp.seek(bloc, os.SEEK_SET) + self.assertEqual(fp.tell(), bloc) + fp.seek(-bloc, os.SEEK_CUR) + self.assertEqual(fp.tell(), 0) + fp.seek(bloc, os.SEEK_CUR) + self.assertEqual(fp.tell(), bloc) + self.assertEqual(fp.read(5), txt[bloc:bloc+5]) + fp.seek(0, os.SEEK_END) + self.assertEqual(fp.tell(), len(txt)) + fp.seek(0, os.SEEK_SET) + self.assertEqual(fp.tell(), 0) + # Check seek on memory file + data = io.BytesIO() + with zipfile.ZipFile(data, mode="w") as zipf: + zipf.writestr("foo.txt", txt) + with zipfile.ZipFile(data, mode="r") as zipf: + with zipf.open("foo.txt", "r") as fp: + fp.seek(bloc, os.SEEK_SET) + self.assertEqual(fp.tell(), bloc) + fp.seek(-bloc, os.SEEK_CUR) + self.assertEqual(fp.tell(), 0) + fp.seek(bloc, os.SEEK_CUR) + self.assertEqual(fp.tell(), bloc) + self.assertEqual(fp.read(5), txt[bloc:bloc+5]) + fp.seek(0, os.SEEK_END) + self.assertEqual(fp.tell(), len(txt)) + fp.seek(0, os.SEEK_SET) + self.assertEqual(fp.tell(), 0) + + @requires_bz2() + def test_decompress_without_3rd_party_library(self): + data = b'PK\x05\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + zip_file = io.BytesIO(data) + with zipfile.ZipFile(zip_file, 'w', compression=zipfile.ZIP_BZIP2) as zf: + zf.writestr('a.txt', b'a') + with mock.patch('zipfile.bz2', None): + with zipfile.ZipFile(zip_file) as zf: + self.assertRaises(RuntimeError, zf.extract, 'a.txt') + + @requires_zlib() + def test_full_overlap(self): + data = ( + b'PK\x03\x04\x14\x00\x00\x00\x08\x00\xa0lH\x05\xe2\x1e' + b'8\xbb\x10\x00\x00\x00\t\x04\x00\x00\x01\x00\x00\x00a\xed' + b'\xc0\x81\x08\x00\x00\x00\xc00\xd6\xfbK\\d\x0b`P' + b'K\x01\x02\x14\x00\x14\x00\x00\x00\x08\x00\xa0lH\x05\xe2' + b'\x1e8\xbb\x10\x00\x00\x00\t\x04\x00\x00\x01\x00\x00\x00\x00' + b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00aPK' + b'\x01\x02\x14\x00\x14\x00\x00\x00\x08\x00\xa0lH\x05\xe2\x1e' + b'8\xbb\x10\x00\x00\x00\t\x04\x00\x00\x01\x00\x00\x00\x00\x00' + b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00bPK\x05' + b'\x06\x00\x00\x00\x00\x02\x00\x02\x00^\x00\x00\x00/\x00\x00' + b'\x00\x00\x00' + ) + with zipfile.ZipFile(io.BytesIO(data), 'r') as zipf: + self.assertEqual(zipf.namelist(), ['a', 'b']) + zi = zipf.getinfo('a') + self.assertEqual(zi.header_offset, 0) + self.assertEqual(zi.compress_size, 16) + self.assertEqual(zi.file_size, 1033) + zi = zipf.getinfo('b') + self.assertEqual(zi.header_offset, 0) + self.assertEqual(zi.compress_size, 16) + self.assertEqual(zi.file_size, 1033) + self.assertEqual(len(zipf.read('a')), 1033) + with self.assertRaisesRegex(zipfile.BadZipFile, 'File name.*differ'): + zipf.read('b') + + @requires_zlib() + def test_quoted_overlap(self): + data = ( + b'PK\x03\x04\x14\x00\x00\x00\x08\x00\xa0lH\x05Y\xfc' + b'8\x044\x00\x00\x00(\x04\x00\x00\x01\x00\x00\x00a\x00' + b'\x1f\x00\xe0\xffPK\x03\x04\x14\x00\x00\x00\x08\x00\xa0l' + b'H\x05\xe2\x1e8\xbb\x10\x00\x00\x00\t\x04\x00\x00\x01\x00' + b'\x00\x00b\xed\xc0\x81\x08\x00\x00\x00\xc00\xd6\xfbK\\' + b'd\x0b`PK\x01\x02\x14\x00\x14\x00\x00\x00\x08\x00\xa0' + b'lH\x05Y\xfc8\x044\x00\x00\x00(\x04\x00\x00\x01' + b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + b'\x00aPK\x01\x02\x14\x00\x14\x00\x00\x00\x08\x00\xa0l' + b'H\x05\xe2\x1e8\xbb\x10\x00\x00\x00\t\x04\x00\x00\x01\x00' + b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00$\x00\x00\x00' + b'bPK\x05\x06\x00\x00\x00\x00\x02\x00\x02\x00^\x00\x00' + b'\x00S\x00\x00\x00\x00\x00' + ) + with zipfile.ZipFile(io.BytesIO(data), 'r') as zipf: + self.assertEqual(zipf.namelist(), ['a', 'b']) + zi = zipf.getinfo('a') + self.assertEqual(zi.header_offset, 0) + self.assertEqual(zi.compress_size, 52) + self.assertEqual(zi.file_size, 1064) + zi = zipf.getinfo('b') + self.assertEqual(zi.header_offset, 36) + self.assertEqual(zi.compress_size, 16) + self.assertEqual(zi.file_size, 1033) + with self.assertRaisesRegex(zipfile.BadZipFile, 'Overlapped entries'): + zipf.read('a') + self.assertEqual(len(zipf.read('b')), 1033) + + def tearDown(self): + unlink(TESTFN) + unlink(TESTFN2) + + +class AbstractBadCrcTests: + def test_testzip_with_bad_crc(self): + """Tests that files with bad CRCs return their name from testzip.""" + zipdata = self.zip_with_bad_crc + + with zipfile.ZipFile(io.BytesIO(zipdata), mode="r") as zipf: + # testzip returns the name of the first corrupt file, or None + self.assertEqual('afile', zipf.testzip()) + + def test_read_with_bad_crc(self): + """Tests that files with bad CRCs raise a BadZipFile exception when read.""" + zipdata = self.zip_with_bad_crc + + # Using ZipFile.read() + with zipfile.ZipFile(io.BytesIO(zipdata), mode="r") as zipf: + self.assertRaises(zipfile.BadZipFile, zipf.read, 'afile') + + # Using ZipExtFile.read() + with zipfile.ZipFile(io.BytesIO(zipdata), mode="r") as zipf: + with zipf.open('afile', 'r') as corrupt_file: + self.assertRaises(zipfile.BadZipFile, corrupt_file.read) + + # Same with small reads (in order to exercise the buffering logic) + with zipfile.ZipFile(io.BytesIO(zipdata), mode="r") as zipf: + with zipf.open('afile', 'r') as corrupt_file: + corrupt_file.MIN_READ_SIZE = 2 + with self.assertRaises(zipfile.BadZipFile): + while corrupt_file.read(2): + pass + + +class StoredBadCrcTests(AbstractBadCrcTests, unittest.TestCase): + compression = zipfile.ZIP_STORED + zip_with_bad_crc = ( + b'PK\003\004\024\0\0\0\0\0 \213\212;:r' + b'\253\377\f\0\0\0\f\0\0\0\005\0\0\000af' + b'ilehello,AworldP' + b'K\001\002\024\003\024\0\0\0\0\0 \213\212;:' + b'r\253\377\f\0\0\0\f\0\0\0\005\0\0\0\0' + b'\0\0\0\0\0\0\0\200\001\0\0\0\000afi' + b'lePK\005\006\0\0\0\0\001\0\001\0003\000' + b'\0\0/\0\0\0\0\0') + +@requires_zlib() +class DeflateBadCrcTests(AbstractBadCrcTests, unittest.TestCase): + compression = zipfile.ZIP_DEFLATED + zip_with_bad_crc = ( + b'PK\x03\x04\x14\x00\x00\x00\x08\x00n}\x0c=FA' + b'KE\x10\x00\x00\x00n\x00\x00\x00\x05\x00\x00\x00af' + b'ile\xcbH\xcd\xc9\xc9W(\xcf/\xcaI\xc9\xa0' + b'=\x13\x00PK\x01\x02\x14\x03\x14\x00\x00\x00\x08\x00n' + b'}\x0c=FAKE\x10\x00\x00\x00n\x00\x00\x00\x05' + b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x01\x00\x00\x00' + b'\x00afilePK\x05\x06\x00\x00\x00\x00\x01\x00' + b'\x01\x003\x00\x00\x003\x00\x00\x00\x00\x00') + +@requires_bz2() +class Bzip2BadCrcTests(AbstractBadCrcTests, unittest.TestCase): + compression = zipfile.ZIP_BZIP2 + zip_with_bad_crc = ( + b'PK\x03\x04\x14\x03\x00\x00\x0c\x00nu\x0c=FA' + b'KE8\x00\x00\x00n\x00\x00\x00\x05\x00\x00\x00af' + b'ileBZh91AY&SY\xd4\xa8\xca' + b'\x7f\x00\x00\x0f\x11\x80@\x00\x06D\x90\x80 \x00 \xa5' + b'P\xd9!\x03\x03\x13\x13\x13\x89\xa9\xa9\xc2u5:\x9f' + b'\x8b\xb9"\x9c(HjTe?\x80PK\x01\x02\x14' + b'\x03\x14\x03\x00\x00\x0c\x00nu\x0c=FAKE8' + b'\x00\x00\x00n\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x00' + b'\x00 \x80\x80\x81\x00\x00\x00\x00afilePK' + b'\x05\x06\x00\x00\x00\x00\x01\x00\x01\x003\x00\x00\x00[\x00' + b'\x00\x00\x00\x00') + +@requires_lzma() +class LzmaBadCrcTests(AbstractBadCrcTests, unittest.TestCase): + compression = zipfile.ZIP_LZMA + zip_with_bad_crc = ( + b'PK\x03\x04\x14\x03\x00\x00\x0e\x00nu\x0c=FA' + b'KE\x1b\x00\x00\x00n\x00\x00\x00\x05\x00\x00\x00af' + b'ile\t\x04\x05\x00]\x00\x00\x00\x04\x004\x19I' + b'\xee\x8d\xe9\x17\x89:3`\tq!.8\x00PK' + b'\x01\x02\x14\x03\x14\x03\x00\x00\x0e\x00nu\x0c=FA' + b'KE\x1b\x00\x00\x00n\x00\x00\x00\x05\x00\x00\x00\x00\x00' + b'\x00\x00\x00\x00 \x80\x80\x81\x00\x00\x00\x00afil' + b'ePK\x05\x06\x00\x00\x00\x00\x01\x00\x01\x003\x00\x00' + b'\x00>\x00\x00\x00\x00\x00') + + +class DecryptionTests(unittest.TestCase): + """Check that ZIP decryption works. Since the library does not + support encryption at the moment, we use a pre-generated encrypted + ZIP file.""" + + data = ( + b'PK\x03\x04\x14\x00\x01\x00\x00\x00n\x92i.#y\xef?&\x00\x00\x00\x1a\x00' + b'\x00\x00\x08\x00\x00\x00test.txt\xfa\x10\xa0gly|\xfa-\xc5\xc0=\xf9y' + b'\x18\xe0\xa8r\xb3Z}Lg\xbc\xae\xf9|\x9b\x19\xe4\x8b\xba\xbb)\x8c\xb0\xdbl' + b'PK\x01\x02\x14\x00\x14\x00\x01\x00\x00\x00n\x92i.#y\xef?&\x00\x00\x00' + b'\x1a\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00\x00\x01\x00 \x00\xb6\x81' + b'\x00\x00\x00\x00test.txtPK\x05\x06\x00\x00\x00\x00\x01\x00\x01\x006\x00' + b'\x00\x00L\x00\x00\x00\x00\x00' ) + data2 = ( + b'PK\x03\x04\x14\x00\t\x00\x08\x00\xcf}38xu\xaa\xb2\x14\x00\x00\x00\x00\x02' + b'\x00\x00\x04\x00\x15\x00zeroUT\t\x00\x03\xd6\x8b\x92G\xda\x8b\x92GUx\x04' + b'\x00\xe8\x03\xe8\x03\xc7:8: EncodingWarning:") + self.assertRegex(warnings[1], rb"^:9: EncodingWarning:") + + 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', encoding="utf-8") 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() + + @pass_alpharep + def test_open_binary_invalid_args(self, alpharep): + root = zipfile.Path(alpharep) + with self.assertRaises(ValueError): + root.joinpath('a.txt').open('rb', encoding='utf-8') + with self.assertRaises(ValueError): + root.joinpath('a.txt').open('rb', 'utf-8') + + 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() + + @pass_alpharep + def test_read(self, alpharep): + root = zipfile.Path(alpharep) + a, b, g = root.iterdir() + assert a.read_text(encoding="utf-8") == "content of a" + a.read_text("utf-8") # No positional arg TypeError per gh-101144. + assert a.read_bytes() == b"content of a" + + @pass_alpharep + def test_joinpath(self, alpharep): + root = zipfile.Path(alpharep) + a = root.joinpath("a.txt") + assert a.is_file() + e = root.joinpath("b").joinpath("d").joinpath("e.txt") + assert e.read_text(encoding="utf-8") == "content of e" + + @pass_alpharep + def test_joinpath_multiple(self, alpharep): + root = zipfile.Path(alpharep) + e = root.joinpath("b", "d", "e.txt") + assert e.read_text(encoding="utf-8") == "content of e" + + @pass_alpharep + def test_traverse_truediv(self, alpharep): + root = zipfile.Path(alpharep) + a = root / "a.txt" + assert a.is_file() + e = root / "b" / "d" / "e.txt" + assert e.read_text(encoding="utf-8") == "content of e" + + @pass_alpharep + def test_traverse_simplediv(self, alpharep): + """ + Disable the __future__.division when testing traversal. + """ + code = compile( + source="zipfile.Path(alpharep) / 'a'", + filename="(test)", + mode="eval", + dont_inherit=True, + ) + eval(code) + + @pass_alpharep + def test_pathlike_construction(self, alpharep): + """ + zipfile.Path should be constructable from a path-like object + """ + zipfile_ondisk = self.zipfile_ondisk(alpharep) + pathlike = pathlib.Path(str(zipfile_ondisk)) + zipfile.Path(pathlike) + + @pass_alpharep + def test_traverse_pathlike(self, alpharep): + root = zipfile.Path(alpharep) + root / pathlib.Path("a") + + @pass_alpharep + def test_parent(self, alpharep): + root = zipfile.Path(alpharep) + assert (root / 'a').parent.at == '' + assert (root / 'a' / 'b').parent.at == 'a/' + + @pass_alpharep + def test_dir_parent(self, alpharep): + root = zipfile.Path(alpharep) + assert (root / 'b').parent.at == '' + assert (root / 'b/').parent.at == '' + + @pass_alpharep + def test_missing_dir_parent(self, alpharep): + root = zipfile.Path(alpharep) + assert (root / 'missing dir/').parent.at == '' + + @pass_alpharep + def test_mutability(self, alpharep): + """ + If the underlying zipfile is changed, the Path object should + reflect that change. + """ + root = zipfile.Path(alpharep) + a, b, g = root.iterdir() + alpharep.writestr('foo.txt', 'foo') + alpharep.writestr('bar/baz.txt', 'baz') + assert any(child.name == 'foo.txt' for child in root.iterdir()) + assert (root / 'foo.txt').read_text(encoding="utf-8") == 'foo' + (baz,) = (root / 'bar').iterdir() + assert baz.read_text(encoding="utf-8") == 'baz' + + HUGE_ZIPFILE_NUM_ENTRIES = 2 ** 13 + + def huge_zipfile(self): + """Create a read-only zipfile with a huge number of entries entries.""" + strm = io.BytesIO() + zf = zipfile.ZipFile(strm, "w") + for entry in map(str, range(self.HUGE_ZIPFILE_NUM_ENTRIES)): + zf.writestr(entry, entry) + zf.mode = 'r' + return zf + + def test_joinpath_constant_time(self): + """ + Ensure joinpath on items in zipfile is linear time. + """ + root = zipfile.Path(self.huge_zipfile()) + entries = jaraco.itertools.Counter(root.iterdir()) + for entry in entries: + entry.joinpath('suffix') + # Check the file iterated all items + assert entries.count == self.HUGE_ZIPFILE_NUM_ENTRIES + + # @func_timeout.func_set_timeout(3) + def test_implied_dirs_performance(self): + data = ['/'.join(string.ascii_lowercase + str(n)) for n in range(10000)] + zipfile.CompleteDirs._implied_dirs(data) + + @pass_alpharep + def test_read_does_not_close(self, alpharep): + alpharep = self.zipfile_ondisk(alpharep) + with zipfile.ZipFile(alpharep) as file: + for rep in range(2): + zipfile.Path(file, 'a.txt').read_text(encoding="utf-8") + + @pass_alpharep + def test_subclass(self, alpharep): + class Subclass(zipfile.Path): + pass + + root = Subclass(alpharep) + assert isinstance(root / 'b', Subclass) + + @pass_alpharep + def test_filename(self, alpharep): + root = zipfile.Path(alpharep) + assert root.filename == pathlib.Path('alpharep.zip') + + @pass_alpharep + def test_root_name(self, alpharep): + """ + The name of the root should be the name of the zipfile + """ + root = zipfile.Path(alpharep) + assert root.name == 'alpharep.zip' == root.filename.name + + @pass_alpharep + def test_root_parent(self, alpharep): + root = zipfile.Path(alpharep) + assert root.parent == pathlib.Path('.') + root.root.filename = 'foo/bar.zip' + assert root.parent == pathlib.Path('foo') + + @pass_alpharep + def test_root_unnamed(self, alpharep): + """ + It is an error to attempt to get the name + or parent of an unnamed zipfile. + """ + alpharep.filename = None + root = zipfile.Path(alpharep) + with self.assertRaises(TypeError): + root.name + with self.assertRaises(TypeError): + root.parent + + # .name and .parent should still work on subs + sub = root / "b" + assert sub.name == "b" + assert sub.parent + + @pass_alpharep + def test_inheritance(self, alpharep): + cls = type('PathChild', (zipfile.Path,), {}) + for alpharep in self.zipfile_alpharep(): + file = cls(alpharep).joinpath('some dir').parent + assert isinstance(file, cls) + + @pass_alpharep + def test_extract_orig_with_implied_dirs(self, alpharep): + """ + A zip file wrapped in a Path should extract even with implied dirs. + """ + source_path = self.zipfile_ondisk(alpharep) + zf = zipfile.ZipFile(source_path) + # wrap the zipfile for its side effect + zipfile.Path(zf) + zf.extractall(source_path.parent) + + def test_malformed_paths(self): + """ + Path should handle malformed paths gracefully. + + Paths with leading slashes are not visible. + + Paths with dots are treated like regular files. + """ + data = io.BytesIO() + zf = zipfile.ZipFile(data, "w") + zf.writestr("/one-slash.txt", b"content") + zf.writestr("//two-slash.txt", b"content") + zf.writestr("../parent.txt", b"content") + zf.filename = '' + root = zipfile.Path(zf) + assert list(map(str, root.iterdir())) == ['../'] + assert root.joinpath('..').joinpath('parent.txt').read_bytes() == b'content' + + def test_unsupported_names(self): + """ + Path segments with special characters are readable. + + On some platforms or file systems, characters like + ``:`` and ``?`` are not allowed, but they are valid + in the zip file. + """ + data = io.BytesIO() + zf = zipfile.ZipFile(data, "w") + zf.writestr("path?", b"content") + zf.writestr("V: NMS.flac", b"fLaC...") + zf.filename = '' + root = zipfile.Path(zf) + contents = root.iterdir() + assert next(contents).name == 'path?' + item = next(contents) + assert item.name == 'V: NMS.flac', item.name + assert root.joinpath('V: NMS.flac').read_bytes() == b"fLaC..." + + def test_backslash_not_separator(self): + """ + In a zip file, backslashes are not separators. + """ + data = io.BytesIO() + zf = zipfile.ZipFile(data, "w") + zf.writestr(DirtyZipInfo.for_name("foo\\bar", zf), b"content") + zf.filename = '' + root = zipfile.Path(zf) + (first,) = root.iterdir() + assert not first.is_dir() + assert first.name == 'foo\\bar', first.name + + +class DirtyZipInfo(zipfile.ZipInfo): + """ + Bypass name sanitization. + """ + + def __init__(self, filename, *args, **kwargs): + super().__init__(filename, *args, **kwargs) + self.filename = filename + + @classmethod + def for_name(cls, name, archive): + """ + Construct the same way that ZipFile.writestr does. + + TODO: extract this functionality and re-use + """ + self = cls(filename=name, date_time=time.localtime(time.time())[:6]) + self.compress_type = archive.compression + self.compress_level = archive.compresslevel + if self.filename.endswith('/'): # pragma: no cover + self.external_attr = 0o40775 << 16 # drwxrwxr-x + self.external_attr |= 0x10 # MS-DOS directory flag + else: + self.external_attr = 0o600 << 16 # ?rw------- + return self + + +class StripExtraTests(unittest.TestCase): + # Note: all of the "z" characters are technically invalid, but up + # to 3 bytes at the end of the extra will be passed through as they + # are too short to encode a valid extra. + + ZIP64_EXTRA = 1 + + def test_no_data(self): + s = struct.Struct("", "exec") +raise_src = 'def do_raise(): raise TypeError\n' + +def make_pyc(co, mtime, size): + data = marshal.dumps(co) + pyc = (importlib.util.MAGIC_NUMBER + + struct.pack("", "exec"), NOW, len(src)) + files = {TESTMOD + pyc_ext: (NOW, pyc), + "some.data": (NOW, "some data")} + self.doTest(pyc_ext, files, TESTMOD) + + def testDefaultOptimizationLevel(self): + # zipimport should use the default optimization level (#28131) + src = """if 1: # indent hack + def test(val): + assert(val) + return val\n""" + files = {TESTMOD + '.py': (NOW, src)} + self.makeZip(files) + sys.path.insert(0, TEMP_ZIP) + mod = importlib.import_module(TESTMOD) + self.assertEqual(mod.test(1), 1) + if __debug__: + self.assertRaises(AssertionError, mod.test, False) + else: + self.assertEqual(mod.test(0), 0) + + def testImport_WithStuff(self): + # try importing from a zipfile which contains additional + # stuff at the beginning of the file + files = {TESTMOD + ".py": (NOW, test_src)} + self.doTest(".py", files, TESTMOD, + stuff=b"Some Stuff"*31) + + def assertModuleSource(self, module): + self.assertEqual(inspect.getsource(module), test_src) + + def testGetSource(self): + files = {TESTMOD + ".py": (NOW, test_src)} + self.doTest(".py", files, TESTMOD, call=self.assertModuleSource) + + def testGetCompiledSource(self): + pyc = make_pyc(compile(test_src, "", "exec"), NOW, len(test_src)) + files = {TESTMOD + ".py": (NOW, test_src), + TESTMOD + pyc_ext: (NOW, pyc)} + self.doTest(pyc_ext, files, TESTMOD, call=self.assertModuleSource) + + def runDoctest(self, callback): + files = {TESTMOD + ".py": (NOW, test_src), + "xyz.txt": (NOW, ">>> log.append(True)\n")} + self.doTest(".py", files, TESTMOD, call=callback) + + def doDoctestFile(self, module): + log = [] + old_master, doctest.master = doctest.master, None + try: + doctest.testfile( + 'xyz.txt', package=module, module_relative=True, + globs=locals() + ) + finally: + doctest.master = old_master + self.assertEqual(log,[True]) + + def testDoctestFile(self): + self.runDoctest(self.doDoctestFile) + + def doDoctestSuite(self, module): + log = [] + doctest.DocFileTest( + 'xyz.txt', package=module, module_relative=True, + globs=locals() + ).run() + self.assertEqual(log,[True]) + + def testDoctestSuite(self): + self.runDoctest(self.doDoctestSuite) + + def doTraceback(self, module): + try: + module.do_raise() + except Exception as e: + tb = e.__traceback__.tb_next + + f,lno,n,line = extract_tb(tb, 1)[0] + self.assertEqual(line, raise_src.strip()) + + f,lno,n,line = extract_stack(tb.tb_frame, 1)[0] + self.assertEqual(line, raise_src.strip()) + + s = io.StringIO() + print_tb(tb, 1, s) + self.assertTrue(s.getvalue().endswith(raise_src)) + else: + raise AssertionError("This ought to be impossible") + + def testTraceback(self): + files = {TESTMOD + ".py": (NOW, raise_src)} + self.doTest(None, files, TESTMOD, call=self.doTraceback) + + @unittest.skipIf(os_helper.TESTFN_UNENCODABLE is None, + "need an unencodable filename") + def testUnencodable(self): + filename = os_helper.TESTFN_UNENCODABLE + ".zip" + self.addCleanup(os_helper.unlink, filename) + with ZipFile(filename, "w") as z: + zinfo = ZipInfo(TESTMOD + ".py", time.localtime(NOW)) + zinfo.compress_type = self.compression + z.writestr(zinfo, test_src) + spec = zipimport.zipimporter(filename).find_spec(TESTMOD) + mod = importlib.util.module_from_spec(spec) + spec.loader.exec_module(mod) + + def testBytesPath(self): + filename = os_helper.TESTFN + ".zip" + self.addCleanup(os_helper.unlink, filename) + with ZipFile(filename, "w") as z: + zinfo = ZipInfo(TESTMOD + ".py", time.localtime(NOW)) + zinfo.compress_type = self.compression + z.writestr(zinfo, test_src) + + zipimport.zipimporter(filename) + zipimport.zipimporter(os.fsencode(filename)) + with self.assertRaises(TypeError): + zipimport.zipimporter(bytearray(os.fsencode(filename))) + with self.assertRaises(TypeError): + zipimport.zipimporter(memoryview(os.fsencode(filename))) + + def testComment(self): + files = {TESTMOD + ".py": (NOW, test_src)} + self.doTest(".py", files, TESTMOD, comment=b"comment") + + def testBeginningCruftAndComment(self): + files = {TESTMOD + ".py": (NOW, test_src)} + self.doTest(".py", files, TESTMOD, stuff=b"cruft" * 64, comment=b"hi") + + def testLargestPossibleComment(self): + files = {TESTMOD + ".py": (NOW, test_src)} + self.doTest(".py", files, TESTMOD, comment=b"c" * ((1 << 16) - 1)) + + +@support.requires_zlib() +class CompressedZipImportTestCase(UncompressedZipImportTestCase): + compression = ZIP_DEFLATED + + +class BadFileZipImportTestCase(unittest.TestCase): + def assertZipFailure(self, filename): + self.assertRaises(zipimport.ZipImportError, + zipimport.zipimporter, filename) + + def testNoFile(self): + self.assertZipFailure('AdfjdkFJKDFJjdklfjs') + + def testEmptyFilename(self): + self.assertZipFailure('') + + def testBadArgs(self): + self.assertRaises(TypeError, zipimport.zipimporter, None) + self.assertRaises(TypeError, zipimport.zipimporter, TESTMOD, kwd=None) + self.assertRaises(TypeError, zipimport.zipimporter, + list(os.fsencode(TESTMOD))) + + def testFilenameTooLong(self): + self.assertZipFailure('A' * 33000) + + def testEmptyFile(self): + os_helper.unlink(TESTMOD) + os_helper.create_empty_file(TESTMOD) + self.assertZipFailure(TESTMOD) + + def testFileUnreadable(self): + os_helper.unlink(TESTMOD) + fd = os.open(TESTMOD, os.O_CREAT, 000) + try: + os.close(fd) + + with self.assertRaises(zipimport.ZipImportError) as cm: + zipimport.zipimporter(TESTMOD) + finally: + # If we leave "the read-only bit" set on Windows, nothing can + # delete TESTMOD, and later tests suffer bogus failures. + os.chmod(TESTMOD, 0o666) + os_helper.unlink(TESTMOD) + + def testNotZipFile(self): + os_helper.unlink(TESTMOD) + fp = open(TESTMOD, 'w+') + fp.write('a' * 22) + fp.close() + self.assertZipFailure(TESTMOD) + + # XXX: disabled until this works on Big-endian machines + def _testBogusZipFile(self): + os_helper.unlink(TESTMOD) + fp = open(TESTMOD, 'w+') + fp.write(struct.pack('=I', 0x06054B50)) + fp.write('a' * 18) + fp.close() + z = zipimport.zipimporter(TESTMOD) + + try: + with warnings.catch_warnings(): + warnings.simplefilter("ignore", DeprecationWarning) + self.assertRaises(TypeError, z.load_module, None) + self.assertRaises(TypeError, z.find_module, None) + self.assertRaises(TypeError, z.find_spec, None) + self.assertRaises(TypeError, z.exec_module, None) + self.assertRaises(TypeError, z.is_package, None) + self.assertRaises(TypeError, z.get_code, None) + self.assertRaises(TypeError, z.get_data, None) + self.assertRaises(TypeError, z.get_source, None) + + error = zipimport.ZipImportError + self.assertIsNone(z.find_module('abc')) + self.assertIsNone(z.find_spec('abc')) + + with warnings.catch_warnings(): + warnings.simplefilter("ignore", DeprecationWarning) + self.assertRaises(error, z.load_module, 'abc') + self.assertRaises(error, z.get_code, 'abc') + self.assertRaises(OSError, z.get_data, 'abc') + self.assertRaises(error, z.get_source, 'abc') + self.assertRaises(error, z.is_package, 'abc') + finally: + zipimport._zip_directory_cache.clear() + + +def tearDownModule(): + os_helper.unlink(TESTMOD) + + +if __name__ == "__main__": + unittest.main() diff --git a/cpython-tests/cp310/test_zipimport_support.py b/cpython-tests/cp310/test_zipimport_support.py new file mode 100644 index 0000000..7bf50a3 --- /dev/null +++ b/cpython-tests/cp310/test_zipimport_support.py @@ -0,0 +1,243 @@ +# This test module covers support in various parts of the standard library +# for working with modules located inside zipfiles +# The tests are centralised in this fashion to make it easy to drop them +# if a platform doesn't support zipimport +import test.support +import os +import os.path +import sys +import textwrap +import zipfile +import zipimport +import doctest +import inspect +import linecache +import unittest +from test.support import os_helper +from test.support.script_helper import (spawn_python, kill_python, assert_python_ok, + make_script, make_zip_script) + +verbose = test.support.verbose + +# Library modules covered by this test set +# pdb (Issue 4201) +# inspect (Issue 4223) +# doctest (Issue 4197) + +# Other test modules with zipimport related tests +# test_zipimport (of course!) +# test_cmd_line_script (covers the zipimport support in runpy) + +# Retrieve some helpers from other test cases +from test import (test_doctest, sample_doctest, sample_doctest_no_doctests, + sample_doctest_no_docstrings) + + +def _run_object_doctest(obj, module): + finder = doctest.DocTestFinder(verbose=verbose, recurse=False) + runner = doctest.DocTestRunner(verbose=verbose) + # Use the object's fully qualified name if it has one + # Otherwise, use the module's name + try: + name = "%s.%s" % (obj.__module__, obj.__qualname__) + except AttributeError: + name = module.__name__ + for example in finder.find(obj, name, module): + runner.run(example) + f, t = runner.failures, runner.tries + if f: + raise test.support.TestFailed("%d of %d doctests failed" % (f, t)) + if verbose: + print ('doctest (%s) ... %d tests with zero failures' % (module.__name__, t)) + return f, t + + + +class ZipSupportTests(unittest.TestCase): + # This used to use the ImportHooksBaseTestCase to restore + # the state of the import related information + # in the sys module after each test. However, that restores + # *too much* information and breaks for the invocation + # of test_doctest. So we do our own thing and leave + # sys.modules alone. + # We also clear the linecache and zipimport cache + # just to avoid any bogus errors due to name reuse in the tests + def setUp(self): + linecache.clearcache() + zipimport._zip_directory_cache.clear() + self.path = sys.path[:] + self.meta_path = sys.meta_path[:] + self.path_hooks = sys.path_hooks[:] + sys.path_importer_cache.clear() + + def tearDown(self): + sys.path[:] = self.path + sys.meta_path[:] = self.meta_path + sys.path_hooks[:] = self.path_hooks + sys.path_importer_cache.clear() + + def test_inspect_getsource_issue4223(self): + test_src = "def foo(): pass\n" + with os_helper.temp_dir() as d: + init_name = make_script(d, '__init__', test_src) + name_in_zip = os.path.join('zip_pkg', + os.path.basename(init_name)) + zip_name, run_name = make_zip_script(d, 'test_zip', + init_name, name_in_zip) + os.remove(init_name) + sys.path.insert(0, zip_name) + import zip_pkg + try: + self.assertEqual(inspect.getsource(zip_pkg.foo), test_src) + finally: + del sys.modules["zip_pkg"] + + def test_doctest_issue4197(self): + # To avoid having to keep two copies of the doctest module's + # unit tests in sync, this test works by taking the source of + # test_doctest itself, rewriting it a bit to cope with a new + # location, and then throwing it in a zip file to make sure + # everything still works correctly + test_src = inspect.getsource(test_doctest) + test_src = test_src.replace( + "from test import test_doctest", + "import test_zipped_doctest as test_doctest") + test_src = test_src.replace("test.test_doctest", + "test_zipped_doctest") + test_src = test_src.replace("test.sample_doctest", + "sample_zipped_doctest") + # The sample doctest files rewritten to include in the zipped version. + sample_sources = {} + for mod in [sample_doctest, sample_doctest_no_doctests, + sample_doctest_no_docstrings]: + src = inspect.getsource(mod) + src = src.replace("test.test_doctest", "test_zipped_doctest") + # Rewrite the module name so that, for example, + # "test.sample_doctest" becomes "sample_zipped_doctest". + mod_name = mod.__name__.split(".")[-1] + mod_name = mod_name.replace("sample_", "sample_zipped_") + sample_sources[mod_name] = src + + with os_helper.temp_dir() as d: + script_name = make_script(d, 'test_zipped_doctest', + test_src) + zip_name, run_name = make_zip_script(d, 'test_zip', + script_name) + with zipfile.ZipFile(zip_name, 'a') as z: + for mod_name, src in sample_sources.items(): + z.writestr(mod_name + ".py", src) + if verbose: + with zipfile.ZipFile(zip_name, 'r') as zip_file: + print ('Contents of %r:' % zip_name) + zip_file.printdir() + os.remove(script_name) + sys.path.insert(0, zip_name) + import test_zipped_doctest + try: + # Some of the doc tests depend on the colocated text files + # which aren't available to the zipped version (the doctest + # module currently requires real filenames for non-embedded + # tests). So we're forced to be selective about which tests + # to run. + # doctest could really use some APIs which take a text + # string or a file object instead of a filename... + known_good_tests = [ + test_zipped_doctest.SampleClass, + test_zipped_doctest.SampleClass.NestedClass, + test_zipped_doctest.SampleClass.NestedClass.__init__, + test_zipped_doctest.SampleClass.__init__, + test_zipped_doctest.SampleClass.a_classmethod, + test_zipped_doctest.SampleClass.a_property, + test_zipped_doctest.SampleClass.a_staticmethod, + test_zipped_doctest.SampleClass.double, + test_zipped_doctest.SampleClass.get, + test_zipped_doctest.SampleNewStyleClass, + test_zipped_doctest.SampleNewStyleClass.__init__, + test_zipped_doctest.SampleNewStyleClass.double, + test_zipped_doctest.SampleNewStyleClass.get, + test_zipped_doctest.sample_func, + test_zipped_doctest.test_DocTest, + test_zipped_doctest.test_DocTestParser, + test_zipped_doctest.test_DocTestRunner.basics, + test_zipped_doctest.test_DocTestRunner.exceptions, + test_zipped_doctest.test_DocTestRunner.option_directives, + test_zipped_doctest.test_DocTestRunner.optionflags, + test_zipped_doctest.test_DocTestRunner.verbose_flag, + test_zipped_doctest.test_Example, + test_zipped_doctest.test_debug, + test_zipped_doctest.test_testsource, + test_zipped_doctest.test_trailing_space_in_test, + test_zipped_doctest.test_DocTestSuite, + test_zipped_doctest.test_DocTestFinder, + ] + # These tests are the ones which need access + # to the data files, so we don't run them + fail_due_to_missing_data_files = [ + test_zipped_doctest.test_DocFileSuite, + test_zipped_doctest.test_testfile, + test_zipped_doctest.test_unittest_reportflags, + ] + + for obj in known_good_tests: + _run_object_doctest(obj, test_zipped_doctest) + finally: + del sys.modules["test_zipped_doctest"] + + def test_doctest_main_issue4197(self): + test_src = textwrap.dedent("""\ + class Test: + ">>> 'line 2'" + pass + + import doctest + doctest.testmod() + """) + pattern = 'File "%s", line 2, in %s' + with os_helper.temp_dir() as d: + script_name = make_script(d, 'script', test_src) + rc, out, err = assert_python_ok(script_name) + expected = pattern % (script_name, "__main__.Test") + if verbose: + print ("Expected line", expected) + print ("Got stdout:") + print (ascii(out)) + self.assertIn(expected.encode('utf-8'), out) + zip_name, run_name = make_zip_script(d, "test_zip", + script_name, '__main__.py') + rc, out, err = assert_python_ok(zip_name) + expected = pattern % (run_name, "__main__.Test") + if verbose: + print ("Expected line", expected) + print ("Got stdout:") + print (ascii(out)) + self.assertIn(expected.encode('utf-8'), out) + + def test_pdb_issue4201(self): + test_src = textwrap.dedent("""\ + def f(): + pass + + import pdb + pdb.Pdb(nosigint=True).runcall(f) + """) + with os_helper.temp_dir() as d: + script_name = make_script(d, 'script', test_src) + p = spawn_python(script_name) + p.stdin.write(b'l\n') + data = kill_python(p) + # bdb/pdb applies normcase to its filename before displaying + self.assertIn(os.path.normcase(script_name.encode('utf-8')), data) + zip_name, run_name = make_zip_script(d, "test_zip", + script_name, '__main__.py') + p = spawn_python(zip_name) + p.stdin.write(b'l\n') + data = kill_python(p) + # bdb/pdb applies normcase to its filename before displaying + self.assertIn(os.path.normcase(run_name.encode('utf-8')), data) + + +def tearDownModule(): + test.support.reap_children() + +if __name__ == '__main__': + unittest.main() diff --git a/cpython-test/cp312/zip_cp437_header.zip b/cpython-tests/cp310/zip_cp437_header.zip similarity index 100% rename from cpython-test/cp312/zip_cp437_header.zip rename to cpython-tests/cp310/zip_cp437_header.zip diff --git a/cpython-test/cp312/zipdir.zip b/cpython-tests/cp310/zipdir.zip similarity index 100% rename from cpython-test/cp312/zipdir.zip rename to cpython-tests/cp310/zipdir.zip diff --git a/cpython-tests/cp310/ziptestdata/README.md b/cpython-tests/cp310/ziptestdata/README.md new file mode 100644 index 0000000..6b9147d --- /dev/null +++ b/cpython-tests/cp310/ziptestdata/README.md @@ -0,0 +1,35 @@ +# Test data for `test_zipfile` + +The test executables in this directory are created manually from header.sh and +the `testdata_module_inside_zip.py` file. You must have infozip's zip utility +installed (`apt install zip` on Debian). + +## Purpose + +These are used to test executable files with an appended zipfile, in a scenario +where the executable is _not_ a Python interpreter itself so our automatic +zipimport machinery (that'd look for `__main__.py`) is not being used. + +## Updating the test executables + +If you update header.sh or the testdata_module_inside_zip.py file, rerun the +commands below. These are expected to be rarely changed, if ever. + +### Standard old format (2.0) zip file + +``` +zip -0 zip2.zip testdata_module_inside_zip.py +cat header.sh zip2.zip >exe_with_zip +rm zip2.zip +``` + +### Modern format (4.5) zip64 file + +Redirecting from stdin forces infozip's zip tool to create a zip64. + +``` +zip -0 zip64.zip +cat header.sh zip64.zip >exe_with_z64 +rm zip64.zip +``` + diff --git a/cpython-test/cp312/ziptestdata/exe_with_z64 b/cpython-tests/cp310/ziptestdata/exe_with_z64 similarity index 100% rename from cpython-test/cp312/ziptestdata/exe_with_z64 rename to cpython-tests/cp310/ziptestdata/exe_with_z64 diff --git a/cpython-test/cp312/ziptestdata/exe_with_zip b/cpython-tests/cp310/ziptestdata/exe_with_zip similarity index 100% rename from cpython-test/cp312/ziptestdata/exe_with_zip rename to cpython-tests/cp310/ziptestdata/exe_with_zip diff --git a/cpython-test/cp312/ziptestdata/header.sh b/cpython-tests/cp310/ziptestdata/header.sh similarity index 100% rename from cpython-test/cp312/ziptestdata/header.sh rename to cpython-tests/cp310/ziptestdata/header.sh diff --git a/cpython-test/cp312/ziptestdata/testdata_module_inside_zip.py b/cpython-tests/cp310/ziptestdata/testdata_module_inside_zip.py similarity index 100% rename from cpython-test/cp312/ziptestdata/testdata_module_inside_zip.py rename to cpython-tests/cp310/ziptestdata/testdata_module_inside_zip.py diff --git a/cpython-tests/cp311/test_zipfile.py b/cpython-tests/cp311/test_zipfile.py new file mode 100644 index 0000000..f4c11d8 --- /dev/null +++ b/cpython-tests/cp311/test_zipfile.py @@ -0,0 +1,3425 @@ +import array +import contextlib +import importlib.util +import io +import itertools +import os +import pathlib +import posixpath +import string +import struct +import subprocess +import sys +import time +import unittest +import unittest.mock as mock +import zipfile +import functools + + +from tempfile import TemporaryFile +from random import randint, random, randbytes + +from test.support import script_helper +from test.support import ( + findfile, requires_zlib, requires_bz2, requires_lzma, + captured_stdout, captured_stderr, requires_subprocess +) +from test.support.os_helper import ( + TESTFN, unlink, rmtree, temp_dir, temp_cwd, fd_count +) + + +TESTFN2 = TESTFN + "2" +TESTFNDIR = TESTFN + "d" +FIXEDTEST_SIZE = 1000 +DATAFILES_DIR = 'zipfile_datafiles' + +SMALL_TEST_DATA = [('_ziptest1', '1q2w3e4r5t'), + ('ziptest2dir/_ziptest2', 'qawsedrftg'), + ('ziptest2dir/ziptest3dir/_ziptest3', 'azsxdcfvgb'), + ('ziptest2dir/ziptest3dir/ziptest4dir/_ziptest3', '6y7u8i9o0p')] + +def get_files(test): + yield TESTFN2 + with TemporaryFile() as f: + yield f + test.assertFalse(f.closed) + with io.BytesIO() as f: + yield f + test.assertFalse(f.closed) + +class AbstractTestsWithSourceFile: + @classmethod + def setUpClass(cls): + cls.line_gen = [bytes("Zipfile test line %d. random float: %f\n" % + (i, random()), "ascii") + for i in range(FIXEDTEST_SIZE)] + cls.data = b''.join(cls.line_gen) + + def setUp(self): + # Make a source file with some lines + with open(TESTFN, "wb") as fp: + fp.write(self.data) + + def make_test_archive(self, f, compression, compresslevel=None): + kwargs = {'compression': compression, 'compresslevel': compresslevel} + # Create the ZIP archive + with zipfile.ZipFile(f, "w", **kwargs) as zipfp: + zipfp.write(TESTFN, "another.name") + zipfp.write(TESTFN, TESTFN) + zipfp.writestr("strfile", self.data) + with zipfp.open('written-open-w', mode='w') as f: + for line in self.line_gen: + f.write(line) + + def zip_test(self, f, compression, compresslevel=None): + self.make_test_archive(f, compression, compresslevel) + + # Read the ZIP archive + with zipfile.ZipFile(f, "r", compression) as zipfp: + self.assertEqual(zipfp.read(TESTFN), self.data) + self.assertEqual(zipfp.read("another.name"), self.data) + self.assertEqual(zipfp.read("strfile"), self.data) + + # Print the ZIP directory + fp = io.StringIO() + zipfp.printdir(file=fp) + directory = fp.getvalue() + lines = directory.splitlines() + self.assertEqual(len(lines), 5) # Number of files + header + + self.assertIn('File Name', lines[0]) + self.assertIn('Modified', lines[0]) + self.assertIn('Size', lines[0]) + + fn, date, time_, size = lines[1].split() + self.assertEqual(fn, 'another.name') + self.assertTrue(time.strptime(date, '%Y-%m-%d')) + self.assertTrue(time.strptime(time_, '%H:%M:%S')) + self.assertEqual(size, str(len(self.data))) + + # Check the namelist + names = zipfp.namelist() + self.assertEqual(len(names), 4) + self.assertIn(TESTFN, names) + self.assertIn("another.name", names) + self.assertIn("strfile", names) + self.assertIn("written-open-w", names) + + # Check infolist + infos = zipfp.infolist() + names = [i.filename for i in infos] + self.assertEqual(len(names), 4) + self.assertIn(TESTFN, names) + self.assertIn("another.name", names) + self.assertIn("strfile", names) + self.assertIn("written-open-w", names) + for i in infos: + self.assertEqual(i.file_size, len(self.data)) + + # check getinfo + for nm in (TESTFN, "another.name", "strfile", "written-open-w"): + info = zipfp.getinfo(nm) + self.assertEqual(info.filename, nm) + self.assertEqual(info.file_size, len(self.data)) + + # Check that testzip doesn't raise an exception + zipfp.testzip() + + def test_basic(self): + for f in get_files(self): + self.zip_test(f, self.compression) + + def zip_open_test(self, f, compression): + self.make_test_archive(f, compression) + + # Read the ZIP archive + with zipfile.ZipFile(f, "r", compression) as zipfp: + zipdata1 = [] + with zipfp.open(TESTFN) as zipopen1: + while True: + read_data = zipopen1.read(256) + if not read_data: + break + zipdata1.append(read_data) + + zipdata2 = [] + with zipfp.open("another.name") as zipopen2: + while True: + read_data = zipopen2.read(256) + if not read_data: + break + zipdata2.append(read_data) + + self.assertEqual(b''.join(zipdata1), self.data) + self.assertEqual(b''.join(zipdata2), self.data) + + def test_open(self): + for f in get_files(self): + self.zip_open_test(f, self.compression) + + def test_open_with_pathlike(self): + path = pathlib.Path(TESTFN2) + self.zip_open_test(path, self.compression) + with zipfile.ZipFile(path, "r", self.compression) as zipfp: + self.assertIsInstance(zipfp.filename, str) + + def zip_random_open_test(self, f, compression): + self.make_test_archive(f, compression) + + # Read the ZIP archive + with zipfile.ZipFile(f, "r", compression) as zipfp: + zipdata1 = [] + with zipfp.open(TESTFN) as zipopen1: + while True: + read_data = zipopen1.read(randint(1, 1024)) + if not read_data: + break + zipdata1.append(read_data) + + self.assertEqual(b''.join(zipdata1), self.data) + + def test_random_open(self): + for f in get_files(self): + self.zip_random_open_test(f, self.compression) + + def zip_read1_test(self, f, compression): + self.make_test_archive(f, compression) + + # Read the ZIP archive + with zipfile.ZipFile(f, "r") as zipfp, \ + zipfp.open(TESTFN) as zipopen: + zipdata = [] + while True: + read_data = zipopen.read1(-1) + if not read_data: + break + zipdata.append(read_data) + + self.assertEqual(b''.join(zipdata), self.data) + + def test_read1(self): + for f in get_files(self): + self.zip_read1_test(f, self.compression) + + def zip_read1_10_test(self, f, compression): + self.make_test_archive(f, compression) + + # Read the ZIP archive + with zipfile.ZipFile(f, "r") as zipfp, \ + zipfp.open(TESTFN) as zipopen: + zipdata = [] + while True: + read_data = zipopen.read1(10) + self.assertLessEqual(len(read_data), 10) + if not read_data: + break + zipdata.append(read_data) + + self.assertEqual(b''.join(zipdata), self.data) + + def test_read1_10(self): + for f in get_files(self): + self.zip_read1_10_test(f, self.compression) + + def zip_readline_read_test(self, f, compression): + self.make_test_archive(f, compression) + + # Read the ZIP archive + with zipfile.ZipFile(f, "r") as zipfp, \ + zipfp.open(TESTFN) as zipopen: + data = b'' + while True: + read = zipopen.readline() + if not read: + break + data += read + + read = zipopen.read(100) + if not read: + break + data += read + + self.assertEqual(data, self.data) + + def test_readline_read(self): + # Issue #7610: calls to readline() interleaved with calls to read(). + for f in get_files(self): + self.zip_readline_read_test(f, self.compression) + + def zip_readline_test(self, f, compression): + self.make_test_archive(f, compression) + + # Read the ZIP archive + with zipfile.ZipFile(f, "r") as zipfp: + with zipfp.open(TESTFN) as zipopen: + for line in self.line_gen: + linedata = zipopen.readline() + self.assertEqual(linedata, line) + + def test_readline(self): + for f in get_files(self): + self.zip_readline_test(f, self.compression) + + def zip_readlines_test(self, f, compression): + self.make_test_archive(f, compression) + + # Read the ZIP archive + with zipfile.ZipFile(f, "r") as zipfp: + with zipfp.open(TESTFN) as zipopen: + ziplines = zipopen.readlines() + for line, zipline in zip(self.line_gen, ziplines): + self.assertEqual(zipline, line) + + def test_readlines(self): + for f in get_files(self): + self.zip_readlines_test(f, self.compression) + + def zip_iterlines_test(self, f, compression): + self.make_test_archive(f, compression) + + # Read the ZIP archive + with zipfile.ZipFile(f, "r") as zipfp: + with zipfp.open(TESTFN) as zipopen: + for line, zipline in zip(self.line_gen, zipopen): + self.assertEqual(zipline, line) + + def test_iterlines(self): + for f in get_files(self): + self.zip_iterlines_test(f, self.compression) + + def test_low_compression(self): + """Check for cases where compressed data is larger than original.""" + # Create the ZIP archive + with zipfile.ZipFile(TESTFN2, "w", self.compression) as zipfp: + zipfp.writestr("strfile", '12') + + # Get an open object for strfile + with zipfile.ZipFile(TESTFN2, "r", self.compression) as zipfp: + with zipfp.open("strfile") as openobj: + self.assertEqual(openobj.read(1), b'1') + self.assertEqual(openobj.read(1), b'2') + + def test_writestr_compression(self): + zipfp = zipfile.ZipFile(TESTFN2, "w") + zipfp.writestr("b.txt", "hello world", compress_type=self.compression) + info = zipfp.getinfo('b.txt') + self.assertEqual(info.compress_type, self.compression) + + def test_writestr_compresslevel(self): + zipfp = zipfile.ZipFile(TESTFN2, "w", compresslevel=1) + zipfp.writestr("a.txt", "hello world", compress_type=self.compression) + zipfp.writestr("b.txt", "hello world", compress_type=self.compression, + compresslevel=2) + + # Compression level follows the constructor. + a_info = zipfp.getinfo('a.txt') + self.assertEqual(a_info.compress_type, self.compression) + self.assertEqual(a_info._compresslevel, 1) + + # Compression level is overridden. + b_info = zipfp.getinfo('b.txt') + self.assertEqual(b_info.compress_type, self.compression) + self.assertEqual(b_info._compresslevel, 2) + + def test_read_return_size(self): + # Issue #9837: ZipExtFile.read() shouldn't return more bytes + # than requested. + for test_size in (1, 4095, 4096, 4097, 16384): + file_size = test_size + 1 + junk = randbytes(file_size) + with zipfile.ZipFile(io.BytesIO(), "w", self.compression) as zipf: + zipf.writestr('foo', junk) + with zipf.open('foo', 'r') as fp: + buf = fp.read(test_size) + self.assertEqual(len(buf), test_size) + + def test_truncated_zipfile(self): + fp = io.BytesIO() + with zipfile.ZipFile(fp, mode='w') as zipf: + zipf.writestr('strfile', self.data, compress_type=self.compression) + end_offset = fp.tell() + zipfiledata = fp.getvalue() + + fp = io.BytesIO(zipfiledata) + with zipfile.ZipFile(fp) as zipf: + with zipf.open('strfile') as zipopen: + fp.truncate(end_offset - 20) + with self.assertRaises(EOFError): + zipopen.read() + + fp = io.BytesIO(zipfiledata) + with zipfile.ZipFile(fp) as zipf: + with zipf.open('strfile') as zipopen: + fp.truncate(end_offset - 20) + with self.assertRaises(EOFError): + while zipopen.read(100): + pass + + fp = io.BytesIO(zipfiledata) + with zipfile.ZipFile(fp) as zipf: + with zipf.open('strfile') as zipopen: + fp.truncate(end_offset - 20) + with self.assertRaises(EOFError): + while zipopen.read1(100): + pass + + def test_repr(self): + fname = 'file.name' + for f in get_files(self): + with zipfile.ZipFile(f, 'w', self.compression) as zipfp: + zipfp.write(TESTFN, fname) + r = repr(zipfp) + self.assertIn("mode='w'", r) + + with zipfile.ZipFile(f, 'r') as zipfp: + r = repr(zipfp) + if isinstance(f, str): + self.assertIn('filename=%r' % f, r) + else: + self.assertIn('file=%r' % f, r) + self.assertIn("mode='r'", r) + r = repr(zipfp.getinfo(fname)) + self.assertIn('filename=%r' % fname, r) + self.assertIn('filemode=', r) + self.assertIn('file_size=', r) + if self.compression != zipfile.ZIP_STORED: + self.assertIn('compress_type=', r) + self.assertIn('compress_size=', r) + with zipfp.open(fname) as zipopen: + r = repr(zipopen) + self.assertIn('name=%r' % fname, r) + self.assertIn("mode='r'", r) + if self.compression != zipfile.ZIP_STORED: + self.assertIn('compress_type=', r) + self.assertIn('[closed]', repr(zipopen)) + self.assertIn('[closed]', repr(zipfp)) + + def test_compresslevel_basic(self): + for f in get_files(self): + self.zip_test(f, self.compression, compresslevel=9) + + def test_per_file_compresslevel(self): + """Check that files within a Zip archive can have different + compression levels.""" + with zipfile.ZipFile(TESTFN2, "w", compresslevel=1) as zipfp: + zipfp.write(TESTFN, 'compress_1') + zipfp.write(TESTFN, 'compress_9', compresslevel=9) + one_info = zipfp.getinfo('compress_1') + nine_info = zipfp.getinfo('compress_9') + self.assertEqual(one_info._compresslevel, 1) + self.assertEqual(nine_info._compresslevel, 9) + + def test_writing_errors(self): + class BrokenFile(io.BytesIO): + def write(self, data): + nonlocal count + if count is not None: + if count == stop: + raise OSError + count += 1 + super().write(data) + + stop = 0 + while True: + testfile = BrokenFile() + count = None + with zipfile.ZipFile(testfile, 'w', self.compression) as zipfp: + with zipfp.open('file1', 'w') as f: + f.write(b'data1') + count = 0 + try: + with zipfp.open('file2', 'w') as f: + f.write(b'data2') + except OSError: + stop += 1 + else: + break + finally: + count = None + with zipfile.ZipFile(io.BytesIO(testfile.getvalue())) as zipfp: + self.assertEqual(zipfp.namelist(), ['file1']) + self.assertEqual(zipfp.read('file1'), b'data1') + + with zipfile.ZipFile(io.BytesIO(testfile.getvalue())) as zipfp: + self.assertEqual(zipfp.namelist(), ['file1', 'file2']) + self.assertEqual(zipfp.read('file1'), b'data1') + self.assertEqual(zipfp.read('file2'), b'data2') + + + def tearDown(self): + unlink(TESTFN) + unlink(TESTFN2) + + +class StoredTestsWithSourceFile(AbstractTestsWithSourceFile, + unittest.TestCase): + compression = zipfile.ZIP_STORED + test_low_compression = None + + def zip_test_writestr_permissions(self, f, compression): + # Make sure that writestr and open(... mode='w') create files with + # mode 0600, when they are passed a name rather than a ZipInfo + # instance. + + self.make_test_archive(f, compression) + with zipfile.ZipFile(f, "r") as zipfp: + zinfo = zipfp.getinfo('strfile') + self.assertEqual(zinfo.external_attr, 0o600 << 16) + + zinfo2 = zipfp.getinfo('written-open-w') + self.assertEqual(zinfo2.external_attr, 0o600 << 16) + + def test_writestr_permissions(self): + for f in get_files(self): + self.zip_test_writestr_permissions(f, zipfile.ZIP_STORED) + + def test_absolute_arcnames(self): + with zipfile.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED) as zipfp: + zipfp.write(TESTFN, "/absolute") + + with zipfile.ZipFile(TESTFN2, "r", zipfile.ZIP_STORED) as zipfp: + self.assertEqual(zipfp.namelist(), ["absolute"]) + + def test_append_to_zip_file(self): + """Test appending to an existing zipfile.""" + with zipfile.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED) as zipfp: + zipfp.write(TESTFN, TESTFN) + + with zipfile.ZipFile(TESTFN2, "a", zipfile.ZIP_STORED) as zipfp: + zipfp.writestr("strfile", self.data) + self.assertEqual(zipfp.namelist(), [TESTFN, "strfile"]) + + def test_append_to_non_zip_file(self): + """Test appending to an existing file that is not a zipfile.""" + # NOTE: this test fails if len(d) < 22 because of the first + # line "fpin.seek(-22, 2)" in _EndRecData + data = b'I am not a ZipFile!'*10 + with open(TESTFN2, 'wb') as f: + f.write(data) + + with zipfile.ZipFile(TESTFN2, "a", zipfile.ZIP_STORED) as zipfp: + zipfp.write(TESTFN, TESTFN) + + with open(TESTFN2, 'rb') as f: + f.seek(len(data)) + with zipfile.ZipFile(f, "r") as zipfp: + self.assertEqual(zipfp.namelist(), [TESTFN]) + self.assertEqual(zipfp.read(TESTFN), self.data) + with open(TESTFN2, 'rb') as f: + self.assertEqual(f.read(len(data)), data) + zipfiledata = f.read() + with io.BytesIO(zipfiledata) as bio, zipfile.ZipFile(bio) as zipfp: + self.assertEqual(zipfp.namelist(), [TESTFN]) + self.assertEqual(zipfp.read(TESTFN), self.data) + + def test_read_concatenated_zip_file(self): + with io.BytesIO() as bio: + with zipfile.ZipFile(bio, 'w', zipfile.ZIP_STORED) as zipfp: + zipfp.write(TESTFN, TESTFN) + zipfiledata = bio.getvalue() + data = b'I am not a ZipFile!'*10 + with open(TESTFN2, 'wb') as f: + f.write(data) + f.write(zipfiledata) + + with zipfile.ZipFile(TESTFN2) as zipfp: + self.assertEqual(zipfp.namelist(), [TESTFN]) + self.assertEqual(zipfp.read(TESTFN), self.data) + + def test_append_to_concatenated_zip_file(self): + with io.BytesIO() as bio: + with zipfile.ZipFile(bio, 'w', zipfile.ZIP_STORED) as zipfp: + zipfp.write(TESTFN, TESTFN) + zipfiledata = bio.getvalue() + data = b'I am not a ZipFile!'*1000000 + with open(TESTFN2, 'wb') as f: + f.write(data) + f.write(zipfiledata) + + with zipfile.ZipFile(TESTFN2, 'a') as zipfp: + self.assertEqual(zipfp.namelist(), [TESTFN]) + zipfp.writestr('strfile', self.data) + + with open(TESTFN2, 'rb') as f: + self.assertEqual(f.read(len(data)), data) + zipfiledata = f.read() + with io.BytesIO(zipfiledata) as bio, zipfile.ZipFile(bio) as zipfp: + self.assertEqual(zipfp.namelist(), [TESTFN, 'strfile']) + self.assertEqual(zipfp.read(TESTFN), self.data) + self.assertEqual(zipfp.read('strfile'), self.data) + + def test_ignores_newline_at_end(self): + with zipfile.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED) as zipfp: + zipfp.write(TESTFN, TESTFN) + with open(TESTFN2, 'a', encoding='utf-8') as f: + f.write("\r\n\00\00\00") + with zipfile.ZipFile(TESTFN2, "r") as zipfp: + self.assertIsInstance(zipfp, zipfile.ZipFile) + + def test_ignores_stuff_appended_past_comments(self): + with zipfile.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED) as zipfp: + zipfp.comment = b"this is a comment" + zipfp.write(TESTFN, TESTFN) + with open(TESTFN2, 'a', encoding='utf-8') as f: + f.write("abcdef\r\n") + with zipfile.ZipFile(TESTFN2, "r") as zipfp: + self.assertIsInstance(zipfp, zipfile.ZipFile) + self.assertEqual(zipfp.comment, b"this is a comment") + + def test_write_default_name(self): + """Check that calling ZipFile.write without arcname specified + produces the expected result.""" + with zipfile.ZipFile(TESTFN2, "w") as zipfp: + zipfp.write(TESTFN) + with open(TESTFN, "rb") as f: + self.assertEqual(zipfp.read(TESTFN), f.read()) + + def test_io_on_closed_zipextfile(self): + fname = "somefile.txt" + with zipfile.ZipFile(TESTFN2, mode="w") as zipfp: + zipfp.writestr(fname, "bogus") + + with zipfile.ZipFile(TESTFN2, mode="r") as zipfp: + with zipfp.open(fname) as fid: + fid.close() + self.assertRaises(ValueError, fid.read) + self.assertRaises(ValueError, fid.seek, 0) + self.assertRaises(ValueError, fid.tell) + self.assertRaises(ValueError, fid.readable) + self.assertRaises(ValueError, fid.seekable) + + def test_write_to_readonly(self): + """Check that trying to call write() on a readonly ZipFile object + raises a ValueError.""" + with zipfile.ZipFile(TESTFN2, mode="w") as zipfp: + zipfp.writestr("somefile.txt", "bogus") + + with zipfile.ZipFile(TESTFN2, mode="r") as zipfp: + self.assertRaises(ValueError, zipfp.write, TESTFN) + + with zipfile.ZipFile(TESTFN2, mode="r") as zipfp: + with self.assertRaises(ValueError): + zipfp.open(TESTFN, mode='w') + + def test_add_file_before_1980(self): + # Set atime and mtime to 1970-01-01 + os.utime(TESTFN, (0, 0)) + with zipfile.ZipFile(TESTFN2, "w") as zipfp: + self.assertRaises(ValueError, zipfp.write, TESTFN) + + with zipfile.ZipFile(TESTFN2, "w", strict_timestamps=False) as zipfp: + zipfp.write(TESTFN) + zinfo = zipfp.getinfo(TESTFN) + self.assertEqual(zinfo.date_time, (1980, 1, 1, 0, 0, 0)) + + def test_add_file_after_2107(self): + # Set atime and mtime to 2108-12-30 + ts = 4386268800 + try: + time.localtime(ts) + except OverflowError: + self.skipTest(f'time.localtime({ts}) raises OverflowError') + try: + os.utime(TESTFN, (ts, ts)) + except OverflowError: + self.skipTest('Host fs cannot set timestamp to required value.') + + mtime_ns = os.stat(TESTFN).st_mtime_ns + if mtime_ns != (4386268800 * 10**9): + # XFS filesystem is limited to 32-bit timestamp, but the syscall + # didn't fail. Moreover, there is a VFS bug which returns + # a cached timestamp which is different than the value on disk. + # + # Test st_mtime_ns rather than st_mtime to avoid rounding issues. + # + # https://bugzilla.redhat.com/show_bug.cgi?id=1795576 + # https://bugs.python.org/issue39460#msg360952 + self.skipTest(f"Linux VFS/XFS kernel bug detected: {mtime_ns=}") + + with zipfile.ZipFile(TESTFN2, "w") as zipfp: + self.assertRaises(struct.error, zipfp.write, TESTFN) + + with zipfile.ZipFile(TESTFN2, "w", strict_timestamps=False) as zipfp: + zipfp.write(TESTFN) + zinfo = zipfp.getinfo(TESTFN) + self.assertEqual(zinfo.date_time, (2107, 12, 31, 23, 59, 59)) + + +@requires_zlib() +class DeflateTestsWithSourceFile(AbstractTestsWithSourceFile, + unittest.TestCase): + compression = zipfile.ZIP_DEFLATED + + def test_per_file_compression(self): + """Check that files within a Zip archive can have different + compression options.""" + with zipfile.ZipFile(TESTFN2, "w") as zipfp: + zipfp.write(TESTFN, 'storeme', zipfile.ZIP_STORED) + zipfp.write(TESTFN, 'deflateme', zipfile.ZIP_DEFLATED) + sinfo = zipfp.getinfo('storeme') + dinfo = zipfp.getinfo('deflateme') + self.assertEqual(sinfo.compress_type, zipfile.ZIP_STORED) + self.assertEqual(dinfo.compress_type, zipfile.ZIP_DEFLATED) + +@requires_bz2() +class Bzip2TestsWithSourceFile(AbstractTestsWithSourceFile, + unittest.TestCase): + compression = zipfile.ZIP_BZIP2 + +@requires_lzma() +class LzmaTestsWithSourceFile(AbstractTestsWithSourceFile, + unittest.TestCase): + compression = zipfile.ZIP_LZMA + + +class AbstractTestZip64InSmallFiles: + # These tests test the ZIP64 functionality without using large files, + # see test_zipfile64 for proper tests. + + @classmethod + def setUpClass(cls): + line_gen = (bytes("Test of zipfile line %d." % i, "ascii") + for i in range(0, FIXEDTEST_SIZE)) + cls.data = b'\n'.join(line_gen) + + def setUp(self): + self._limit = zipfile.ZIP64_LIMIT + self._filecount_limit = zipfile.ZIP_FILECOUNT_LIMIT + zipfile.ZIP64_LIMIT = 1000 + zipfile.ZIP_FILECOUNT_LIMIT = 9 + + # Make a source file with some lines + with open(TESTFN, "wb") as fp: + fp.write(self.data) + + def zip_test(self, f, compression): + # Create the ZIP archive + with zipfile.ZipFile(f, "w", compression, allowZip64=True) as zipfp: + zipfp.write(TESTFN, "another.name") + zipfp.write(TESTFN, TESTFN) + zipfp.writestr("strfile", self.data) + + # Read the ZIP archive + with zipfile.ZipFile(f, "r", compression) as zipfp: + self.assertEqual(zipfp.read(TESTFN), self.data) + self.assertEqual(zipfp.read("another.name"), self.data) + self.assertEqual(zipfp.read("strfile"), self.data) + + # Print the ZIP directory + fp = io.StringIO() + zipfp.printdir(fp) + + directory = fp.getvalue() + lines = directory.splitlines() + self.assertEqual(len(lines), 4) # Number of files + header + + self.assertIn('File Name', lines[0]) + self.assertIn('Modified', lines[0]) + self.assertIn('Size', lines[0]) + + fn, date, time_, size = lines[1].split() + self.assertEqual(fn, 'another.name') + self.assertTrue(time.strptime(date, '%Y-%m-%d')) + self.assertTrue(time.strptime(time_, '%H:%M:%S')) + self.assertEqual(size, str(len(self.data))) + + # Check the namelist + names = zipfp.namelist() + self.assertEqual(len(names), 3) + self.assertIn(TESTFN, names) + self.assertIn("another.name", names) + self.assertIn("strfile", names) + + # Check infolist + infos = zipfp.infolist() + names = [i.filename for i in infos] + self.assertEqual(len(names), 3) + self.assertIn(TESTFN, names) + self.assertIn("another.name", names) + self.assertIn("strfile", names) + for i in infos: + self.assertEqual(i.file_size, len(self.data)) + + # check getinfo + for nm in (TESTFN, "another.name", "strfile"): + info = zipfp.getinfo(nm) + self.assertEqual(info.filename, nm) + self.assertEqual(info.file_size, len(self.data)) + + # Check that testzip doesn't raise an exception + zipfp.testzip() + + def test_basic(self): + for f in get_files(self): + self.zip_test(f, self.compression) + + def test_too_many_files(self): + # This test checks that more than 64k files can be added to an archive, + # and that the resulting archive can be read properly by ZipFile + zipf = zipfile.ZipFile(TESTFN, "w", self.compression, + allowZip64=True) + zipf.debug = 100 + numfiles = 15 + for i in range(numfiles): + zipf.writestr("foo%08d" % i, "%d" % (i**3 % 57)) + self.assertEqual(len(zipf.namelist()), numfiles) + zipf.close() + + zipf2 = zipfile.ZipFile(TESTFN, "r", self.compression) + self.assertEqual(len(zipf2.namelist()), numfiles) + for i in range(numfiles): + content = zipf2.read("foo%08d" % i).decode('ascii') + self.assertEqual(content, "%d" % (i**3 % 57)) + zipf2.close() + + def test_too_many_files_append(self): + zipf = zipfile.ZipFile(TESTFN, "w", self.compression, + allowZip64=False) + zipf.debug = 100 + numfiles = 9 + for i in range(numfiles): + zipf.writestr("foo%08d" % i, "%d" % (i**3 % 57)) + self.assertEqual(len(zipf.namelist()), numfiles) + with self.assertRaises(zipfile.LargeZipFile): + zipf.writestr("foo%08d" % numfiles, b'') + self.assertEqual(len(zipf.namelist()), numfiles) + zipf.close() + + zipf = zipfile.ZipFile(TESTFN, "a", self.compression, + allowZip64=False) + zipf.debug = 100 + self.assertEqual(len(zipf.namelist()), numfiles) + with self.assertRaises(zipfile.LargeZipFile): + zipf.writestr("foo%08d" % numfiles, b'') + self.assertEqual(len(zipf.namelist()), numfiles) + zipf.close() + + zipf = zipfile.ZipFile(TESTFN, "a", self.compression, + allowZip64=True) + zipf.debug = 100 + self.assertEqual(len(zipf.namelist()), numfiles) + numfiles2 = 15 + for i in range(numfiles, numfiles2): + zipf.writestr("foo%08d" % i, "%d" % (i**3 % 57)) + self.assertEqual(len(zipf.namelist()), numfiles2) + zipf.close() + + zipf2 = zipfile.ZipFile(TESTFN, "r", self.compression) + self.assertEqual(len(zipf2.namelist()), numfiles2) + for i in range(numfiles2): + content = zipf2.read("foo%08d" % i).decode('ascii') + self.assertEqual(content, "%d" % (i**3 % 57)) + zipf2.close() + + def tearDown(self): + zipfile.ZIP64_LIMIT = self._limit + zipfile.ZIP_FILECOUNT_LIMIT = self._filecount_limit + unlink(TESTFN) + unlink(TESTFN2) + + +class StoredTestZip64InSmallFiles(AbstractTestZip64InSmallFiles, + unittest.TestCase): + compression = zipfile.ZIP_STORED + + def large_file_exception_test(self, f, compression): + with zipfile.ZipFile(f, "w", compression, allowZip64=False) as zipfp: + self.assertRaises(zipfile.LargeZipFile, + zipfp.write, TESTFN, "another.name") + + def large_file_exception_test2(self, f, compression): + with zipfile.ZipFile(f, "w", compression, allowZip64=False) as zipfp: + self.assertRaises(zipfile.LargeZipFile, + zipfp.writestr, "another.name", self.data) + + def test_large_file_exception(self): + for f in get_files(self): + self.large_file_exception_test(f, zipfile.ZIP_STORED) + self.large_file_exception_test2(f, zipfile.ZIP_STORED) + + def test_absolute_arcnames(self): + with zipfile.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED, + allowZip64=True) as zipfp: + zipfp.write(TESTFN, "/absolute") + + with zipfile.ZipFile(TESTFN2, "r", zipfile.ZIP_STORED) as zipfp: + self.assertEqual(zipfp.namelist(), ["absolute"]) + + def test_append(self): + # Test that appending to the Zip64 archive doesn't change + # extra fields of existing entries. + with zipfile.ZipFile(TESTFN2, "w", allowZip64=True) as zipfp: + zipfp.writestr("strfile", self.data) + with zipfile.ZipFile(TESTFN2, "r", allowZip64=True) as zipfp: + zinfo = zipfp.getinfo("strfile") + extra = zinfo.extra + with zipfile.ZipFile(TESTFN2, "a", allowZip64=True) as zipfp: + zipfp.writestr("strfile2", self.data) + with zipfile.ZipFile(TESTFN2, "r", allowZip64=True) as zipfp: + zinfo = zipfp.getinfo("strfile") + self.assertEqual(zinfo.extra, extra) + + def make_zip64_file( + self, file_size_64_set=False, file_size_extra=False, + compress_size_64_set=False, compress_size_extra=False, + header_offset_64_set=False, header_offset_extra=False, + ): + """Generate bytes sequence for a zip with (incomplete) zip64 data. + + The actual values (not the zip 64 0xffffffff values) stored in the file + are: + file_size: 8 + compress_size: 8 + header_offset: 0 + """ + actual_size = 8 + actual_header_offset = 0 + local_zip64_fields = [] + central_zip64_fields = [] + + file_size = actual_size + if file_size_64_set: + file_size = 0xffffffff + if file_size_extra: + local_zip64_fields.append(actual_size) + central_zip64_fields.append(actual_size) + file_size = struct.pack("e|f"g?h*i', ','), r'a\b,c_d_e_f_g_h_i') + self.assertEqual(san('../../foo../../ba..r', '/'), r'foo/ba..r') + + def test_extract_hackers_arcnames_common_cases(self): + common_hacknames = [ + ('../foo/bar', 'foo/bar'), + ('foo/../bar', 'foo/bar'), + ('foo/../../bar', 'foo/bar'), + ('foo/bar/..', 'foo/bar'), + ('./../foo/bar', 'foo/bar'), + ('/foo/bar', 'foo/bar'), + ('/foo/../bar', 'foo/bar'), + ('/foo/../../bar', 'foo/bar'), + ] + self._test_extract_hackers_arcnames(common_hacknames) + + @unittest.skipIf(os.path.sep != '\\', 'Requires \\ as path separator.') + def test_extract_hackers_arcnames_windows_only(self): + """Test combination of path fixing and windows name sanitization.""" + windows_hacknames = [ + (r'..\foo\bar', 'foo/bar'), + (r'..\/foo\/bar', 'foo/bar'), + (r'foo/\..\/bar', 'foo/bar'), + (r'foo\/../\bar', 'foo/bar'), + (r'C:foo/bar', 'foo/bar'), + (r'C:/foo/bar', 'foo/bar'), + (r'C://foo/bar', 'foo/bar'), + (r'C:\foo\bar', 'foo/bar'), + (r'//conky/mountpoint/foo/bar', 'foo/bar'), + (r'\\conky\mountpoint\foo\bar', 'foo/bar'), + (r'///conky/mountpoint/foo/bar', 'conky/mountpoint/foo/bar'), + (r'\\\conky\mountpoint\foo\bar', 'conky/mountpoint/foo/bar'), + (r'//conky//mountpoint/foo/bar', 'conky/mountpoint/foo/bar'), + (r'\\conky\\mountpoint\foo\bar', 'conky/mountpoint/foo/bar'), + (r'//?/C:/foo/bar', 'foo/bar'), + (r'\\?\C:\foo\bar', 'foo/bar'), + (r'C:/../C:/foo/bar', 'C_/foo/bar'), + (r'a:b\ce|f"g?h*i', 'b/c_d_e_f_g_h_i'), + ('../../foo../../ba..r', 'foo/ba..r'), + ] + self._test_extract_hackers_arcnames(windows_hacknames) + + @unittest.skipIf(os.path.sep != '/', r'Requires / as path separator.') + def test_extract_hackers_arcnames_posix_only(self): + posix_hacknames = [ + ('//foo/bar', 'foo/bar'), + ('../../foo../../ba..r', 'foo../ba..r'), + (r'foo/..\bar', r'foo/..\bar'), + ] + self._test_extract_hackers_arcnames(posix_hacknames) + + def _test_extract_hackers_arcnames(self, hacknames): + for arcname, fixedname in hacknames: + content = b'foobar' + arcname.encode() + with zipfile.ZipFile(TESTFN2, 'w', zipfile.ZIP_STORED) as zipfp: + zinfo = zipfile.ZipInfo() + # preserve backslashes + zinfo.filename = arcname + zinfo.external_attr = 0o600 << 16 + zipfp.writestr(zinfo, content) + + arcname = arcname.replace(os.sep, "/") + targetpath = os.path.join('target', 'subdir', 'subsub') + correctfile = os.path.join(targetpath, *fixedname.split('/')) + + with zipfile.ZipFile(TESTFN2, 'r') as zipfp: + writtenfile = zipfp.extract(arcname, targetpath) + self.assertEqual(writtenfile, correctfile, + msg='extract %r: %r != %r' % + (arcname, writtenfile, correctfile)) + self.check_file(correctfile, content) + rmtree('target') + + with zipfile.ZipFile(TESTFN2, 'r') as zipfp: + zipfp.extractall(targetpath) + self.check_file(correctfile, content) + rmtree('target') + + correctfile = os.path.join(os.getcwd(), *fixedname.split('/')) + + with zipfile.ZipFile(TESTFN2, 'r') as zipfp: + writtenfile = zipfp.extract(arcname) + self.assertEqual(writtenfile, correctfile, + msg="extract %r" % arcname) + self.check_file(correctfile, content) + rmtree(fixedname.split('/')[0]) + + with zipfile.ZipFile(TESTFN2, 'r') as zipfp: + zipfp.extractall() + self.check_file(correctfile, content) + rmtree(fixedname.split('/')[0]) + + unlink(TESTFN2) + + +class OtherTests(unittest.TestCase): + def test_open_via_zip_info(self): + # Create the ZIP archive + with zipfile.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED) as zipfp: + zipfp.writestr("name", "foo") + with self.assertWarns(UserWarning): + zipfp.writestr("name", "bar") + self.assertEqual(zipfp.namelist(), ["name"] * 2) + + with zipfile.ZipFile(TESTFN2, "r") as zipfp: + infos = zipfp.infolist() + data = b"" + for info in infos: + with zipfp.open(info) as zipopen: + data += zipopen.read() + self.assertIn(data, {b"foobar", b"barfoo"}) + data = b"" + for info in infos: + data += zipfp.read(info) + self.assertIn(data, {b"foobar", b"barfoo"}) + + def test_writestr_extended_local_header_issue1202(self): + with zipfile.ZipFile(TESTFN2, 'w') as orig_zip: + for data in 'abcdefghijklmnop': + zinfo = zipfile.ZipInfo(data) + zinfo.flag_bits |= zipfile._MASK_USE_DATA_DESCRIPTOR # Include an extended local header. + orig_zip.writestr(zinfo, data) + + def test_close(self): + """Check that the zipfile is closed after the 'with' block.""" + with zipfile.ZipFile(TESTFN2, "w") as zipfp: + for fpath, fdata in SMALL_TEST_DATA: + zipfp.writestr(fpath, fdata) + self.assertIsNotNone(zipfp.fp, 'zipfp is not open') + self.assertIsNone(zipfp.fp, 'zipfp is not closed') + + with zipfile.ZipFile(TESTFN2, "r") as zipfp: + self.assertIsNotNone(zipfp.fp, 'zipfp is not open') + self.assertIsNone(zipfp.fp, 'zipfp is not closed') + + def test_close_on_exception(self): + """Check that the zipfile is closed if an exception is raised in the + 'with' block.""" + with zipfile.ZipFile(TESTFN2, "w") as zipfp: + for fpath, fdata in SMALL_TEST_DATA: + zipfp.writestr(fpath, fdata) + + try: + with zipfile.ZipFile(TESTFN2, "r") as zipfp2: + raise zipfile.BadZipFile() + except zipfile.BadZipFile: + self.assertIsNone(zipfp2.fp, 'zipfp is not closed') + + def test_unsupported_version(self): + # File has an extract_version of 120 + data = (b'PK\x03\x04x\x00\x00\x00\x00\x00!p\xa1@\x00\x00\x00\x00\x00\x00' + b'\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00xPK\x01\x02x\x03x\x00\x00\x00\x00' + b'\x00!p\xa1@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00' + b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x01\x00\x00\x00\x00xPK\x05\x06' + b'\x00\x00\x00\x00\x01\x00\x01\x00/\x00\x00\x00\x1f\x00\x00\x00\x00\x00') + + self.assertRaises(NotImplementedError, zipfile.ZipFile, + io.BytesIO(data), 'r') + + @requires_zlib() + def test_read_unicode_filenames(self): + # bug #10801 + fname = findfile('zip_cp437_header.zip') + with zipfile.ZipFile(fname) as zipfp: + for name in zipfp.namelist(): + zipfp.open(name).close() + + def test_write_unicode_filenames(self): + with zipfile.ZipFile(TESTFN, "w") as zf: + zf.writestr("foo.txt", "Test for unicode filename") + zf.writestr("\xf6.txt", "Test for unicode filename") + self.assertIsInstance(zf.infolist()[0].filename, str) + + with zipfile.ZipFile(TESTFN, "r") as zf: + self.assertEqual(zf.filelist[0].filename, "foo.txt") + self.assertEqual(zf.filelist[1].filename, "\xf6.txt") + + def test_read_after_write_unicode_filenames(self): + with zipfile.ZipFile(TESTFN2, 'w') as zipfp: + zipfp.writestr('приклад', b'sample') + self.assertEqual(zipfp.read('приклад'), b'sample') + + def test_exclusive_create_zip_file(self): + """Test exclusive creating a new zipfile.""" + unlink(TESTFN2) + filename = 'testfile.txt' + content = b'hello, world. this is some content.' + with zipfile.ZipFile(TESTFN2, "x", zipfile.ZIP_STORED) as zipfp: + zipfp.writestr(filename, content) + with self.assertRaises(FileExistsError): + zipfile.ZipFile(TESTFN2, "x", zipfile.ZIP_STORED) + with zipfile.ZipFile(TESTFN2, "r") as zipfp: + self.assertEqual(zipfp.namelist(), [filename]) + self.assertEqual(zipfp.read(filename), content) + + def test_create_non_existent_file_for_append(self): + if os.path.exists(TESTFN): + os.unlink(TESTFN) + + filename = 'testfile.txt' + content = b'hello, world. this is some content.' + + try: + with zipfile.ZipFile(TESTFN, 'a') as zf: + zf.writestr(filename, content) + except OSError: + self.fail('Could not append data to a non-existent zip file.') + + self.assertTrue(os.path.exists(TESTFN)) + + with zipfile.ZipFile(TESTFN, 'r') as zf: + self.assertEqual(zf.read(filename), content) + + def test_close_erroneous_file(self): + # This test checks that the ZipFile constructor closes the file object + # it opens if there's an error in the file. If it doesn't, the + # traceback holds a reference to the ZipFile object and, indirectly, + # the file object. + # On Windows, this causes the os.unlink() call to fail because the + # underlying file is still open. This is SF bug #412214. + # + with open(TESTFN, "w", encoding="utf-8") as fp: + fp.write("this is not a legal zip file\n") + try: + zf = zipfile.ZipFile(TESTFN) + except zipfile.BadZipFile: + pass + + def test_is_zip_erroneous_file(self): + """Check that is_zipfile() correctly identifies non-zip files.""" + # - passing a filename + with open(TESTFN, "w", encoding='utf-8') as fp: + fp.write("this is not a legal zip file\n") + self.assertFalse(zipfile.is_zipfile(TESTFN)) + # - passing a path-like object + self.assertFalse(zipfile.is_zipfile(pathlib.Path(TESTFN))) + # - passing a file object + with open(TESTFN, "rb") as fp: + self.assertFalse(zipfile.is_zipfile(fp)) + # - passing a file-like object + fp = io.BytesIO() + fp.write(b"this is not a legal zip file\n") + self.assertFalse(zipfile.is_zipfile(fp)) + fp.seek(0, 0) + self.assertFalse(zipfile.is_zipfile(fp)) + + def test_damaged_zipfile(self): + """Check that zipfiles with missing bytes at the end raise BadZipFile.""" + # - Create a valid zip file + fp = io.BytesIO() + with zipfile.ZipFile(fp, mode="w") as zipf: + zipf.writestr("foo.txt", b"O, for a Muse of Fire!") + zipfiledata = fp.getvalue() + + # - Now create copies of it missing the last N bytes and make sure + # a BadZipFile exception is raised when we try to open it + for N in range(len(zipfiledata)): + fp = io.BytesIO(zipfiledata[:N]) + self.assertRaises(zipfile.BadZipFile, zipfile.ZipFile, fp) + + def test_is_zip_valid_file(self): + """Check that is_zipfile() correctly identifies zip files.""" + # - passing a filename + with zipfile.ZipFile(TESTFN, mode="w") as zipf: + zipf.writestr("foo.txt", b"O, for a Muse of Fire!") + + self.assertTrue(zipfile.is_zipfile(TESTFN)) + # - passing a file object + with open(TESTFN, "rb") as fp: + self.assertTrue(zipfile.is_zipfile(fp)) + fp.seek(0, 0) + zip_contents = fp.read() + # - passing a file-like object + fp = io.BytesIO() + fp.write(zip_contents) + self.assertTrue(zipfile.is_zipfile(fp)) + fp.seek(0, 0) + self.assertTrue(zipfile.is_zipfile(fp)) + + def test_non_existent_file_raises_OSError(self): + # make sure we don't raise an AttributeError when a partially-constructed + # ZipFile instance is finalized; this tests for regression on SF tracker + # bug #403871. + + # The bug we're testing for caused an AttributeError to be raised + # when a ZipFile instance was created for a file that did not + # exist; the .fp member was not initialized but was needed by the + # __del__() method. Since the AttributeError is in the __del__(), + # it is ignored, but the user should be sufficiently annoyed by + # the message on the output that regression will be noticed + # quickly. + self.assertRaises(OSError, zipfile.ZipFile, TESTFN) + + def test_empty_file_raises_BadZipFile(self): + f = open(TESTFN, 'w', encoding='utf-8') + f.close() + self.assertRaises(zipfile.BadZipFile, zipfile.ZipFile, TESTFN) + + with open(TESTFN, 'w', encoding='utf-8') as fp: + fp.write("short file") + self.assertRaises(zipfile.BadZipFile, zipfile.ZipFile, TESTFN) + + def test_negative_central_directory_offset_raises_BadZipFile(self): + # Zip file containing an empty EOCD record + buffer = bytearray(b'PK\x05\x06' + b'\0'*18) + + # Set the size of the central directory bytes to become 1, + # causing the central directory offset to become negative + for dirsize in 1, 2**32-1: + buffer[12:16] = struct.pack(' os.path.getsize(TESTFN)) + with zipfile.ZipFile(TESTFN,mode="r") as zipf: + self.assertEqual(zipf.comment, b"shorter comment") + + def test_unicode_comment(self): + with zipfile.ZipFile(TESTFN, "w", zipfile.ZIP_STORED) as zipf: + zipf.writestr("foo.txt", "O, for a Muse of Fire!") + with self.assertRaises(TypeError): + zipf.comment = "this is an error" + + def test_change_comment_in_empty_archive(self): + with zipfile.ZipFile(TESTFN, "a", zipfile.ZIP_STORED) as zipf: + self.assertFalse(zipf.filelist) + zipf.comment = b"this is a comment" + with zipfile.ZipFile(TESTFN, "r") as zipf: + self.assertEqual(zipf.comment, b"this is a comment") + + def test_change_comment_in_nonempty_archive(self): + with zipfile.ZipFile(TESTFN, "w", zipfile.ZIP_STORED) as zipf: + zipf.writestr("foo.txt", "O, for a Muse of Fire!") + with zipfile.ZipFile(TESTFN, "a", zipfile.ZIP_STORED) as zipf: + self.assertTrue(zipf.filelist) + zipf.comment = b"this is a comment" + with zipfile.ZipFile(TESTFN, "r") as zipf: + self.assertEqual(zipf.comment, b"this is a comment") + + def test_empty_zipfile(self): + # Check that creating a file in 'w' or 'a' mode and closing without + # adding any files to the archives creates a valid empty ZIP file + zipf = zipfile.ZipFile(TESTFN, mode="w") + zipf.close() + try: + zipf = zipfile.ZipFile(TESTFN, mode="r") + except zipfile.BadZipFile: + self.fail("Unable to create empty ZIP file in 'w' mode") + + zipf = zipfile.ZipFile(TESTFN, mode="a") + zipf.close() + try: + zipf = zipfile.ZipFile(TESTFN, mode="r") + except: + self.fail("Unable to create empty ZIP file in 'a' mode") + + def test_open_empty_file(self): + # Issue 1710703: Check that opening a file with less than 22 bytes + # raises a BadZipFile exception (rather than the previously unhelpful + # OSError) + f = open(TESTFN, 'w', encoding='utf-8') + f.close() + self.assertRaises(zipfile.BadZipFile, zipfile.ZipFile, TESTFN, 'r') + + def test_create_zipinfo_before_1980(self): + self.assertRaises(ValueError, + zipfile.ZipInfo, 'seventies', (1979, 1, 1, 0, 0, 0)) + + def test_create_empty_zipinfo_repr(self): + """Before bpo-26185, repr() on empty ZipInfo object was failing.""" + zi = zipfile.ZipInfo(filename="empty") + self.assertEqual(repr(zi), "") + + def test_create_empty_zipinfo_default_attributes(self): + """Ensure all required attributes are set.""" + zi = zipfile.ZipInfo() + self.assertEqual(zi.orig_filename, "NoName") + self.assertEqual(zi.filename, "NoName") + self.assertEqual(zi.date_time, (1980, 1, 1, 0, 0, 0)) + self.assertEqual(zi.compress_type, zipfile.ZIP_STORED) + self.assertEqual(zi.comment, b"") + self.assertEqual(zi.extra, b"") + self.assertIn(zi.create_system, (0, 3)) + self.assertEqual(zi.create_version, zipfile.DEFAULT_VERSION) + self.assertEqual(zi.extract_version, zipfile.DEFAULT_VERSION) + self.assertEqual(zi.reserved, 0) + self.assertEqual(zi.flag_bits, 0) + self.assertEqual(zi.volume, 0) + self.assertEqual(zi.internal_attr, 0) + self.assertEqual(zi.external_attr, 0) + + # Before bpo-26185, both were missing + self.assertEqual(zi.file_size, 0) + self.assertEqual(zi.compress_size, 0) + + def test_zipfile_with_short_extra_field(self): + """If an extra field in the header is less than 4 bytes, skip it.""" + zipdata = ( + b'PK\x03\x04\x14\x00\x00\x00\x00\x00\x93\x9b\xad@\x8b\x9e' + b'\xd9\xd3\x01\x00\x00\x00\x01\x00\x00\x00\x03\x00\x03\x00ab' + b'c\x00\x00\x00APK\x01\x02\x14\x03\x14\x00\x00\x00\x00' + b'\x00\x93\x9b\xad@\x8b\x9e\xd9\xd3\x01\x00\x00\x00\x01\x00\x00' + b'\x00\x03\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x00' + b'\x00\x00\x00abc\x00\x00PK\x05\x06\x00\x00\x00\x00' + b'\x01\x00\x01\x003\x00\x00\x00%\x00\x00\x00\x00\x00' + ) + with zipfile.ZipFile(io.BytesIO(zipdata), 'r') as zipf: + # testzip returns the name of the first corrupt file, or None + self.assertIsNone(zipf.testzip()) + + def test_open_conflicting_handles(self): + # It's only possible to open one writable file handle at a time + msg1 = b"It's fun to charter an accountant!" + msg2 = b"And sail the wide accountant sea" + msg3 = b"To find, explore the funds offshore" + with zipfile.ZipFile(TESTFN2, 'w', zipfile.ZIP_STORED) as zipf: + with zipf.open('foo', mode='w') as w2: + w2.write(msg1) + with zipf.open('bar', mode='w') as w1: + with self.assertRaises(ValueError): + zipf.open('handle', mode='w') + with self.assertRaises(ValueError): + zipf.open('foo', mode='r') + with self.assertRaises(ValueError): + zipf.writestr('str', 'abcde') + with self.assertRaises(ValueError): + zipf.write(__file__, 'file') + with self.assertRaises(ValueError): + zipf.close() + w1.write(msg2) + with zipf.open('baz', mode='w') as w2: + w2.write(msg3) + + with zipfile.ZipFile(TESTFN2, 'r') as zipf: + self.assertEqual(zipf.read('foo'), msg1) + self.assertEqual(zipf.read('bar'), msg2) + self.assertEqual(zipf.read('baz'), msg3) + self.assertEqual(zipf.namelist(), ['foo', 'bar', 'baz']) + + def test_seek_tell(self): + # Test seek functionality + txt = b"Where's Bruce?" + bloc = txt.find(b"Bruce") + # Check seek on a file + with zipfile.ZipFile(TESTFN, "w") as zipf: + zipf.writestr("foo.txt", txt) + with zipfile.ZipFile(TESTFN, "r") as zipf: + with zipf.open("foo.txt", "r") as fp: + fp.seek(bloc, os.SEEK_SET) + self.assertEqual(fp.tell(), bloc) + fp.seek(-bloc, os.SEEK_CUR) + self.assertEqual(fp.tell(), 0) + fp.seek(bloc, os.SEEK_CUR) + self.assertEqual(fp.tell(), bloc) + self.assertEqual(fp.read(5), txt[bloc:bloc+5]) + fp.seek(0, os.SEEK_END) + self.assertEqual(fp.tell(), len(txt)) + fp.seek(0, os.SEEK_SET) + self.assertEqual(fp.tell(), 0) + # Check seek on memory file + data = io.BytesIO() + with zipfile.ZipFile(data, mode="w") as zipf: + zipf.writestr("foo.txt", txt) + with zipfile.ZipFile(data, mode="r") as zipf: + with zipf.open("foo.txt", "r") as fp: + fp.seek(bloc, os.SEEK_SET) + self.assertEqual(fp.tell(), bloc) + fp.seek(-bloc, os.SEEK_CUR) + self.assertEqual(fp.tell(), 0) + fp.seek(bloc, os.SEEK_CUR) + self.assertEqual(fp.tell(), bloc) + self.assertEqual(fp.read(5), txt[bloc:bloc+5]) + fp.seek(0, os.SEEK_END) + self.assertEqual(fp.tell(), len(txt)) + fp.seek(0, os.SEEK_SET) + self.assertEqual(fp.tell(), 0) + + @requires_bz2() + def test_decompress_without_3rd_party_library(self): + data = b'PK\x05\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + zip_file = io.BytesIO(data) + with zipfile.ZipFile(zip_file, 'w', compression=zipfile.ZIP_BZIP2) as zf: + zf.writestr('a.txt', b'a') + with mock.patch('zipfile.bz2', None): + with zipfile.ZipFile(zip_file) as zf: + self.assertRaises(RuntimeError, zf.extract, 'a.txt') + + def tearDown(self): + unlink(TESTFN) + unlink(TESTFN2) + + +class AbstractBadCrcTests: + def test_testzip_with_bad_crc(self): + """Tests that files with bad CRCs return their name from testzip.""" + zipdata = self.zip_with_bad_crc + + with zipfile.ZipFile(io.BytesIO(zipdata), mode="r") as zipf: + # testzip returns the name of the first corrupt file, or None + self.assertEqual('afile', zipf.testzip()) + + def test_read_with_bad_crc(self): + """Tests that files with bad CRCs raise a BadZipFile exception when read.""" + zipdata = self.zip_with_bad_crc + + # Using ZipFile.read() + with zipfile.ZipFile(io.BytesIO(zipdata), mode="r") as zipf: + self.assertRaises(zipfile.BadZipFile, zipf.read, 'afile') + + # Using ZipExtFile.read() + with zipfile.ZipFile(io.BytesIO(zipdata), mode="r") as zipf: + with zipf.open('afile', 'r') as corrupt_file: + self.assertRaises(zipfile.BadZipFile, corrupt_file.read) + + # Same with small reads (in order to exercise the buffering logic) + with zipfile.ZipFile(io.BytesIO(zipdata), mode="r") as zipf: + with zipf.open('afile', 'r') as corrupt_file: + corrupt_file.MIN_READ_SIZE = 2 + with self.assertRaises(zipfile.BadZipFile): + while corrupt_file.read(2): + pass + + +class StoredBadCrcTests(AbstractBadCrcTests, unittest.TestCase): + compression = zipfile.ZIP_STORED + zip_with_bad_crc = ( + b'PK\003\004\024\0\0\0\0\0 \213\212;:r' + b'\253\377\f\0\0\0\f\0\0\0\005\0\0\000af' + b'ilehello,AworldP' + b'K\001\002\024\003\024\0\0\0\0\0 \213\212;:' + b'r\253\377\f\0\0\0\f\0\0\0\005\0\0\0\0' + b'\0\0\0\0\0\0\0\200\001\0\0\0\000afi' + b'lePK\005\006\0\0\0\0\001\0\001\0003\000' + b'\0\0/\0\0\0\0\0') + +@requires_zlib() +class DeflateBadCrcTests(AbstractBadCrcTests, unittest.TestCase): + compression = zipfile.ZIP_DEFLATED + zip_with_bad_crc = ( + b'PK\x03\x04\x14\x00\x00\x00\x08\x00n}\x0c=FA' + b'KE\x10\x00\x00\x00n\x00\x00\x00\x05\x00\x00\x00af' + b'ile\xcbH\xcd\xc9\xc9W(\xcf/\xcaI\xc9\xa0' + b'=\x13\x00PK\x01\x02\x14\x03\x14\x00\x00\x00\x08\x00n' + b'}\x0c=FAKE\x10\x00\x00\x00n\x00\x00\x00\x05' + b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x01\x00\x00\x00' + b'\x00afilePK\x05\x06\x00\x00\x00\x00\x01\x00' + b'\x01\x003\x00\x00\x003\x00\x00\x00\x00\x00') + +@requires_bz2() +class Bzip2BadCrcTests(AbstractBadCrcTests, unittest.TestCase): + compression = zipfile.ZIP_BZIP2 + zip_with_bad_crc = ( + b'PK\x03\x04\x14\x03\x00\x00\x0c\x00nu\x0c=FA' + b'KE8\x00\x00\x00n\x00\x00\x00\x05\x00\x00\x00af' + b'ileBZh91AY&SY\xd4\xa8\xca' + b'\x7f\x00\x00\x0f\x11\x80@\x00\x06D\x90\x80 \x00 \xa5' + b'P\xd9!\x03\x03\x13\x13\x13\x89\xa9\xa9\xc2u5:\x9f' + b'\x8b\xb9"\x9c(HjTe?\x80PK\x01\x02\x14' + b'\x03\x14\x03\x00\x00\x0c\x00nu\x0c=FAKE8' + b'\x00\x00\x00n\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x00' + b'\x00 \x80\x80\x81\x00\x00\x00\x00afilePK' + b'\x05\x06\x00\x00\x00\x00\x01\x00\x01\x003\x00\x00\x00[\x00' + b'\x00\x00\x00\x00') + +@requires_lzma() +class LzmaBadCrcTests(AbstractBadCrcTests, unittest.TestCase): + compression = zipfile.ZIP_LZMA + zip_with_bad_crc = ( + b'PK\x03\x04\x14\x03\x00\x00\x0e\x00nu\x0c=FA' + b'KE\x1b\x00\x00\x00n\x00\x00\x00\x05\x00\x00\x00af' + b'ile\t\x04\x05\x00]\x00\x00\x00\x04\x004\x19I' + b'\xee\x8d\xe9\x17\x89:3`\tq!.8\x00PK' + b'\x01\x02\x14\x03\x14\x03\x00\x00\x0e\x00nu\x0c=FA' + b'KE\x1b\x00\x00\x00n\x00\x00\x00\x05\x00\x00\x00\x00\x00' + b'\x00\x00\x00\x00 \x80\x80\x81\x00\x00\x00\x00afil' + b'ePK\x05\x06\x00\x00\x00\x00\x01\x00\x01\x003\x00\x00' + b'\x00>\x00\x00\x00\x00\x00') + + +class DecryptionTests(unittest.TestCase): + """Check that ZIP decryption works. Since the library does not + support encryption at the moment, we use a pre-generated encrypted + ZIP file.""" + + data = ( + b'PK\x03\x04\x14\x00\x01\x00\x00\x00n\x92i.#y\xef?&\x00\x00\x00\x1a\x00' + b'\x00\x00\x08\x00\x00\x00test.txt\xfa\x10\xa0gly|\xfa-\xc5\xc0=\xf9y' + b'\x18\xe0\xa8r\xb3Z}Lg\xbc\xae\xf9|\x9b\x19\xe4\x8b\xba\xbb)\x8c\xb0\xdbl' + b'PK\x01\x02\x14\x00\x14\x00\x01\x00\x00\x00n\x92i.#y\xef?&\x00\x00\x00' + b'\x1a\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00\x00\x01\x00 \x00\xb6\x81' + b'\x00\x00\x00\x00test.txtPK\x05\x06\x00\x00\x00\x00\x01\x00\x01\x006\x00' + b'\x00\x00L\x00\x00\x00\x00\x00' ) + data2 = ( + b'PK\x03\x04\x14\x00\t\x00\x08\x00\xcf}38xu\xaa\xb2\x14\x00\x00\x00\x00\x02' + b'\x00\x00\x04\x00\x15\x00zeroUT\t\x00\x03\xd6\x8b\x92G\xda\x8b\x92GUx\x04' + b'\x00\xe8\x03\xe8\x03\xc7", "exec") +raise_src = 'def do_raise(): raise TypeError\n' + +def make_pyc(co, mtime, size): + data = marshal.dumps(co) + pyc = (importlib.util.MAGIC_NUMBER + + struct.pack("", "exec"), NOW, len(src)) + files = {TESTMOD + pyc_ext: (NOW, pyc), + "some.data": (NOW, "some data")} + self.doTest(pyc_ext, files, TESTMOD) + + def testDefaultOptimizationLevel(self): + # zipimport should use the default optimization level (#28131) + src = """if 1: # indent hack + def test(val): + assert(val) + return val\n""" + files = {TESTMOD + '.py': (NOW, src)} + self.makeZip(files) + sys.path.insert(0, TEMP_ZIP) + mod = importlib.import_module(TESTMOD) + self.assertEqual(mod.test(1), 1) + if __debug__: + self.assertRaises(AssertionError, mod.test, False) + else: + self.assertEqual(mod.test(0), 0) + + def testImport_WithStuff(self): + # try importing from a zipfile which contains additional + # stuff at the beginning of the file + files = {TESTMOD + ".py": (NOW, test_src)} + self.doTest(".py", files, TESTMOD, + stuff=b"Some Stuff"*31) + + def assertModuleSource(self, module): + self.assertEqual(inspect.getsource(module), test_src) + + def testGetSource(self): + files = {TESTMOD + ".py": (NOW, test_src)} + self.doTest(".py", files, TESTMOD, call=self.assertModuleSource) + + def testGetCompiledSource(self): + pyc = make_pyc(compile(test_src, "", "exec"), NOW, len(test_src)) + files = {TESTMOD + ".py": (NOW, test_src), + TESTMOD + pyc_ext: (NOW, pyc)} + self.doTest(pyc_ext, files, TESTMOD, call=self.assertModuleSource) + + def runDoctest(self, callback): + files = {TESTMOD + ".py": (NOW, test_src), + "xyz.txt": (NOW, ">>> log.append(True)\n")} + self.doTest(".py", files, TESTMOD, call=callback) + + def doDoctestFile(self, module): + log = [] + old_master, doctest.master = doctest.master, None + try: + doctest.testfile( + 'xyz.txt', package=module, module_relative=True, + globs=locals() + ) + finally: + doctest.master = old_master + self.assertEqual(log,[True]) + + def testDoctestFile(self): + self.runDoctest(self.doDoctestFile) + + def doDoctestSuite(self, module): + log = [] + doctest.DocFileTest( + 'xyz.txt', package=module, module_relative=True, + globs=locals() + ).run() + self.assertEqual(log,[True]) + + def testDoctestSuite(self): + self.runDoctest(self.doDoctestSuite) + + def doTraceback(self, module): + try: + module.do_raise() + except Exception as e: + tb = e.__traceback__.tb_next + + f,lno,n,line = extract_tb(tb, 1)[0] + self.assertEqual(line, raise_src.strip()) + + f,lno,n,line = extract_stack(tb.tb_frame, 1)[0] + self.assertEqual(line, raise_src.strip()) + + s = io.StringIO() + print_tb(tb, 1, s) + self.assertTrue(s.getvalue().endswith( + ' def do_raise(): raise TypeError\n' + '' if support.has_no_debug_ranges() else + ' ^^^^^^^^^^^^^^^\n' + )) + else: + raise AssertionError("This ought to be impossible") + + def testTraceback(self): + files = {TESTMOD + ".py": (NOW, raise_src)} + self.doTest(None, files, TESTMOD, call=self.doTraceback) + + @unittest.skipIf(os_helper.TESTFN_UNENCODABLE is None, + "need an unencodable filename") + def testUnencodable(self): + filename = os_helper.TESTFN_UNENCODABLE + ".zip" + self.addCleanup(os_helper.unlink, filename) + with ZipFile(filename, "w") as z: + zinfo = ZipInfo(TESTMOD + ".py", time.localtime(NOW)) + zinfo.compress_type = self.compression + z.writestr(zinfo, test_src) + spec = zipimport.zipimporter(filename).find_spec(TESTMOD) + mod = importlib.util.module_from_spec(spec) + spec.loader.exec_module(mod) + + def testBytesPath(self): + filename = os_helper.TESTFN + ".zip" + self.addCleanup(os_helper.unlink, filename) + with ZipFile(filename, "w") as z: + zinfo = ZipInfo(TESTMOD + ".py", time.localtime(NOW)) + zinfo.compress_type = self.compression + z.writestr(zinfo, test_src) + + zipimport.zipimporter(filename) + with self.assertRaises(TypeError): + zipimport.zipimporter(os.fsencode(filename)) + with self.assertRaises(TypeError): + zipimport.zipimporter(bytearray(os.fsencode(filename))) + with self.assertRaises(TypeError): + zipimport.zipimporter(memoryview(os.fsencode(filename))) + + def testComment(self): + files = {TESTMOD + ".py": (NOW, test_src)} + self.doTest(".py", files, TESTMOD, comment=b"comment") + + def testBeginningCruftAndComment(self): + files = {TESTMOD + ".py": (NOW, test_src)} + self.doTest(".py", files, TESTMOD, stuff=b"cruft" * 64, comment=b"hi") + + def testLargestPossibleComment(self): + files = {TESTMOD + ".py": (NOW, test_src)} + self.doTest(".py", files, TESTMOD, comment=b"c" * ((1 << 16) - 1)) + + +@support.requires_zlib() +class CompressedZipImportTestCase(UncompressedZipImportTestCase): + compression = ZIP_DEFLATED + + +class BadFileZipImportTestCase(unittest.TestCase): + def assertZipFailure(self, filename): + self.assertRaises(zipimport.ZipImportError, + zipimport.zipimporter, filename) + + def testNoFile(self): + self.assertZipFailure('AdfjdkFJKDFJjdklfjs') + + def testEmptyFilename(self): + self.assertZipFailure('') + + def testBadArgs(self): + self.assertRaises(TypeError, zipimport.zipimporter, None) + self.assertRaises(TypeError, zipimport.zipimporter, TESTMOD, kwd=None) + self.assertRaises(TypeError, zipimport.zipimporter, + list(os.fsencode(TESTMOD))) + + def testFilenameTooLong(self): + self.assertZipFailure('A' * 33000) + + def testEmptyFile(self): + os_helper.unlink(TESTMOD) + os_helper.create_empty_file(TESTMOD) + self.assertZipFailure(TESTMOD) + + @unittest.skipIf(support.is_wasi, "mode 000 not supported.") + def testFileUnreadable(self): + os_helper.unlink(TESTMOD) + fd = os.open(TESTMOD, os.O_CREAT, 000) + try: + os.close(fd) + + with self.assertRaises(zipimport.ZipImportError) as cm: + zipimport.zipimporter(TESTMOD) + finally: + # If we leave "the read-only bit" set on Windows, nothing can + # delete TESTMOD, and later tests suffer bogus failures. + os.chmod(TESTMOD, 0o666) + os_helper.unlink(TESTMOD) + + def testNotZipFile(self): + os_helper.unlink(TESTMOD) + fp = open(TESTMOD, 'w+') + fp.write('a' * 22) + fp.close() + self.assertZipFailure(TESTMOD) + + # XXX: disabled until this works on Big-endian machines + def _testBogusZipFile(self): + os_helper.unlink(TESTMOD) + fp = open(TESTMOD, 'w+') + fp.write(struct.pack('=I', 0x06054B50)) + fp.write('a' * 18) + fp.close() + z = zipimport.zipimporter(TESTMOD) + + try: + with warnings.catch_warnings(): + warnings.simplefilter("ignore", DeprecationWarning) + self.assertRaises(TypeError, z.load_module, None) + self.assertRaises(TypeError, z.find_module, None) + self.assertRaises(TypeError, z.find_spec, None) + self.assertRaises(TypeError, z.exec_module, None) + self.assertRaises(TypeError, z.is_package, None) + self.assertRaises(TypeError, z.get_code, None) + self.assertRaises(TypeError, z.get_data, None) + self.assertRaises(TypeError, z.get_source, None) + + error = zipimport.ZipImportError + self.assertIsNone(z.find_module('abc')) + self.assertIsNone(z.find_spec('abc')) + + with warnings.catch_warnings(): + warnings.simplefilter("ignore", DeprecationWarning) + self.assertRaises(error, z.load_module, 'abc') + self.assertRaises(error, z.get_code, 'abc') + self.assertRaises(OSError, z.get_data, 'abc') + self.assertRaises(error, z.get_source, 'abc') + self.assertRaises(error, z.is_package, 'abc') + finally: + zipimport._zip_directory_cache.clear() + + +def tearDownModule(): + os_helper.unlink(TESTMOD) + + +if __name__ == "__main__": + unittest.main() diff --git a/cpython-tests/cp311/test_zipimport_support.py b/cpython-tests/cp311/test_zipimport_support.py new file mode 100644 index 0000000..7bf50a3 --- /dev/null +++ b/cpython-tests/cp311/test_zipimport_support.py @@ -0,0 +1,243 @@ +# This test module covers support in various parts of the standard library +# for working with modules located inside zipfiles +# The tests are centralised in this fashion to make it easy to drop them +# if a platform doesn't support zipimport +import test.support +import os +import os.path +import sys +import textwrap +import zipfile +import zipimport +import doctest +import inspect +import linecache +import unittest +from test.support import os_helper +from test.support.script_helper import (spawn_python, kill_python, assert_python_ok, + make_script, make_zip_script) + +verbose = test.support.verbose + +# Library modules covered by this test set +# pdb (Issue 4201) +# inspect (Issue 4223) +# doctest (Issue 4197) + +# Other test modules with zipimport related tests +# test_zipimport (of course!) +# test_cmd_line_script (covers the zipimport support in runpy) + +# Retrieve some helpers from other test cases +from test import (test_doctest, sample_doctest, sample_doctest_no_doctests, + sample_doctest_no_docstrings) + + +def _run_object_doctest(obj, module): + finder = doctest.DocTestFinder(verbose=verbose, recurse=False) + runner = doctest.DocTestRunner(verbose=verbose) + # Use the object's fully qualified name if it has one + # Otherwise, use the module's name + try: + name = "%s.%s" % (obj.__module__, obj.__qualname__) + except AttributeError: + name = module.__name__ + for example in finder.find(obj, name, module): + runner.run(example) + f, t = runner.failures, runner.tries + if f: + raise test.support.TestFailed("%d of %d doctests failed" % (f, t)) + if verbose: + print ('doctest (%s) ... %d tests with zero failures' % (module.__name__, t)) + return f, t + + + +class ZipSupportTests(unittest.TestCase): + # This used to use the ImportHooksBaseTestCase to restore + # the state of the import related information + # in the sys module after each test. However, that restores + # *too much* information and breaks for the invocation + # of test_doctest. So we do our own thing and leave + # sys.modules alone. + # We also clear the linecache and zipimport cache + # just to avoid any bogus errors due to name reuse in the tests + def setUp(self): + linecache.clearcache() + zipimport._zip_directory_cache.clear() + self.path = sys.path[:] + self.meta_path = sys.meta_path[:] + self.path_hooks = sys.path_hooks[:] + sys.path_importer_cache.clear() + + def tearDown(self): + sys.path[:] = self.path + sys.meta_path[:] = self.meta_path + sys.path_hooks[:] = self.path_hooks + sys.path_importer_cache.clear() + + def test_inspect_getsource_issue4223(self): + test_src = "def foo(): pass\n" + with os_helper.temp_dir() as d: + init_name = make_script(d, '__init__', test_src) + name_in_zip = os.path.join('zip_pkg', + os.path.basename(init_name)) + zip_name, run_name = make_zip_script(d, 'test_zip', + init_name, name_in_zip) + os.remove(init_name) + sys.path.insert(0, zip_name) + import zip_pkg + try: + self.assertEqual(inspect.getsource(zip_pkg.foo), test_src) + finally: + del sys.modules["zip_pkg"] + + def test_doctest_issue4197(self): + # To avoid having to keep two copies of the doctest module's + # unit tests in sync, this test works by taking the source of + # test_doctest itself, rewriting it a bit to cope with a new + # location, and then throwing it in a zip file to make sure + # everything still works correctly + test_src = inspect.getsource(test_doctest) + test_src = test_src.replace( + "from test import test_doctest", + "import test_zipped_doctest as test_doctest") + test_src = test_src.replace("test.test_doctest", + "test_zipped_doctest") + test_src = test_src.replace("test.sample_doctest", + "sample_zipped_doctest") + # The sample doctest files rewritten to include in the zipped version. + sample_sources = {} + for mod in [sample_doctest, sample_doctest_no_doctests, + sample_doctest_no_docstrings]: + src = inspect.getsource(mod) + src = src.replace("test.test_doctest", "test_zipped_doctest") + # Rewrite the module name so that, for example, + # "test.sample_doctest" becomes "sample_zipped_doctest". + mod_name = mod.__name__.split(".")[-1] + mod_name = mod_name.replace("sample_", "sample_zipped_") + sample_sources[mod_name] = src + + with os_helper.temp_dir() as d: + script_name = make_script(d, 'test_zipped_doctest', + test_src) + zip_name, run_name = make_zip_script(d, 'test_zip', + script_name) + with zipfile.ZipFile(zip_name, 'a') as z: + for mod_name, src in sample_sources.items(): + z.writestr(mod_name + ".py", src) + if verbose: + with zipfile.ZipFile(zip_name, 'r') as zip_file: + print ('Contents of %r:' % zip_name) + zip_file.printdir() + os.remove(script_name) + sys.path.insert(0, zip_name) + import test_zipped_doctest + try: + # Some of the doc tests depend on the colocated text files + # which aren't available to the zipped version (the doctest + # module currently requires real filenames for non-embedded + # tests). So we're forced to be selective about which tests + # to run. + # doctest could really use some APIs which take a text + # string or a file object instead of a filename... + known_good_tests = [ + test_zipped_doctest.SampleClass, + test_zipped_doctest.SampleClass.NestedClass, + test_zipped_doctest.SampleClass.NestedClass.__init__, + test_zipped_doctest.SampleClass.__init__, + test_zipped_doctest.SampleClass.a_classmethod, + test_zipped_doctest.SampleClass.a_property, + test_zipped_doctest.SampleClass.a_staticmethod, + test_zipped_doctest.SampleClass.double, + test_zipped_doctest.SampleClass.get, + test_zipped_doctest.SampleNewStyleClass, + test_zipped_doctest.SampleNewStyleClass.__init__, + test_zipped_doctest.SampleNewStyleClass.double, + test_zipped_doctest.SampleNewStyleClass.get, + test_zipped_doctest.sample_func, + test_zipped_doctest.test_DocTest, + test_zipped_doctest.test_DocTestParser, + test_zipped_doctest.test_DocTestRunner.basics, + test_zipped_doctest.test_DocTestRunner.exceptions, + test_zipped_doctest.test_DocTestRunner.option_directives, + test_zipped_doctest.test_DocTestRunner.optionflags, + test_zipped_doctest.test_DocTestRunner.verbose_flag, + test_zipped_doctest.test_Example, + test_zipped_doctest.test_debug, + test_zipped_doctest.test_testsource, + test_zipped_doctest.test_trailing_space_in_test, + test_zipped_doctest.test_DocTestSuite, + test_zipped_doctest.test_DocTestFinder, + ] + # These tests are the ones which need access + # to the data files, so we don't run them + fail_due_to_missing_data_files = [ + test_zipped_doctest.test_DocFileSuite, + test_zipped_doctest.test_testfile, + test_zipped_doctest.test_unittest_reportflags, + ] + + for obj in known_good_tests: + _run_object_doctest(obj, test_zipped_doctest) + finally: + del sys.modules["test_zipped_doctest"] + + def test_doctest_main_issue4197(self): + test_src = textwrap.dedent("""\ + class Test: + ">>> 'line 2'" + pass + + import doctest + doctest.testmod() + """) + pattern = 'File "%s", line 2, in %s' + with os_helper.temp_dir() as d: + script_name = make_script(d, 'script', test_src) + rc, out, err = assert_python_ok(script_name) + expected = pattern % (script_name, "__main__.Test") + if verbose: + print ("Expected line", expected) + print ("Got stdout:") + print (ascii(out)) + self.assertIn(expected.encode('utf-8'), out) + zip_name, run_name = make_zip_script(d, "test_zip", + script_name, '__main__.py') + rc, out, err = assert_python_ok(zip_name) + expected = pattern % (run_name, "__main__.Test") + if verbose: + print ("Expected line", expected) + print ("Got stdout:") + print (ascii(out)) + self.assertIn(expected.encode('utf-8'), out) + + def test_pdb_issue4201(self): + test_src = textwrap.dedent("""\ + def f(): + pass + + import pdb + pdb.Pdb(nosigint=True).runcall(f) + """) + with os_helper.temp_dir() as d: + script_name = make_script(d, 'script', test_src) + p = spawn_python(script_name) + p.stdin.write(b'l\n') + data = kill_python(p) + # bdb/pdb applies normcase to its filename before displaying + self.assertIn(os.path.normcase(script_name.encode('utf-8')), data) + zip_name, run_name = make_zip_script(d, "test_zip", + script_name, '__main__.py') + p = spawn_python(zip_name) + p.stdin.write(b'l\n') + data = kill_python(p) + # bdb/pdb applies normcase to its filename before displaying + self.assertIn(os.path.normcase(run_name.encode('utf-8')), data) + + +def tearDownModule(): + test.support.reap_children() + +if __name__ == '__main__': + unittest.main() diff --git a/cpython-tests/cp311/zip_cp437_header.zip b/cpython-tests/cp311/zip_cp437_header.zip new file mode 100644 index 0000000000000000000000000000000000000000..f7c6cf170422c48ce1337a4c05843fe66a4b0bbf GIT binary patch literal 270 zcmWIWW@Zs#U}E54IGj1(Hjhth3p)b?11k`V0&!YqPHJ9aZfbmaW=Tf;#QZkBl8Tbz zujP-QGBE_;)G30lGrzP1MI$4V2m@|Qfx3Y}0Zig>LV!1JBS5x8fC7-R0%8=FkcTh5&DbW=z*2 uYc>UI2Du%m9b!Lz?W_zi?FI&&mdJ6*Ca-0n$rA90maKRyZ91 literal 0 HcmV?d00001 diff --git a/cpython-tests/cp311/ziptestdata/README.md b/cpython-tests/cp311/ziptestdata/README.md new file mode 100644 index 0000000..6b9147d --- /dev/null +++ b/cpython-tests/cp311/ziptestdata/README.md @@ -0,0 +1,35 @@ +# Test data for `test_zipfile` + +The test executables in this directory are created manually from header.sh and +the `testdata_module_inside_zip.py` file. You must have infozip's zip utility +installed (`apt install zip` on Debian). + +## Purpose + +These are used to test executable files with an appended zipfile, in a scenario +where the executable is _not_ a Python interpreter itself so our automatic +zipimport machinery (that'd look for `__main__.py`) is not being used. + +## Updating the test executables + +If you update header.sh or the testdata_module_inside_zip.py file, rerun the +commands below. These are expected to be rarely changed, if ever. + +### Standard old format (2.0) zip file + +``` +zip -0 zip2.zip testdata_module_inside_zip.py +cat header.sh zip2.zip >exe_with_zip +rm zip2.zip +``` + +### Modern format (4.5) zip64 file + +Redirecting from stdin forces infozip's zip tool to create a zip64. + +``` +zip -0 zip64.zip +cat header.sh zip64.zip >exe_with_z64 +rm zip64.zip +``` + diff --git a/cpython-tests/cp311/ziptestdata/exe_with_z64 b/cpython-tests/cp311/ziptestdata/exe_with_z64 new file mode 100644 index 0000000000000000000000000000000000000000..82b03cf39d919d9de05866b4f7cdf1dfe47e6eb3 GIT binary patch literal 978 zcmaJ<-EY${5KkEl$xl4ME4rnV(xNp330}q~w6RoFg|_N41e7XrQ)jhEoXByzLbV6} z7bN~nxXZ`b5aOcPclPh+`)uy)&!pO)@qEF01K%5u#vZQ0`QQ{+-#hbQJlMzfN zumhbn*t?s5Bd=_jPG5pq2*m(Jgo_mHo-#sbTHp%FGB+?21c5M360YVDOC^Boi)A8| zaqW`1mIj`)NHXt(_xjvFK6&e598YZ!YZ3l8f{q6rI6U+Qr@^orj6V8rh65&(EY$|m zyw<+SERwNcOz}kI84m>G zSHN@NP(AKCZFVWm;@bWsvo1d0s^NQ(q;qlPXs1m?Of5j_0ahSNH4rM0DoR1B`pzXg zmbq!Q2?j9dhGVD|)zyN}i{}esyQ-xKTZG$#>tt`JC1{4sF91y#s`x7`^Rh*e)YvZy zgkqqkaUCw?O1P{lg45-zR7)d3Et45`xQsPi8a|7~fpf#r#O@xyAC7yz7Yxqdop@t= z+GecTY{GH*C{6^9iZVG|$~dMm;TcwVF6O`^njW)|c@d2ZNMpBKJnC=V?N}s_K0g`u zf>&q1Drr~`txm&wV0p#0b-g#i7nomB!y-wOlGog%8hujhFq@*CrC0V>0$BJLY}9Yu zdAxPoGdZHaQ8}dT$9GygqyF~x9%(2wjr1B?@B4I!vMx6ZdG|^ES=ode_3v$y*}#wR GCH6PMMGy`E literal 0 HcmV?d00001 diff --git a/cpython-tests/cp311/ziptestdata/exe_with_zip b/cpython-tests/cp311/ziptestdata/exe_with_zip new file mode 100644 index 0000000000000000000000000000000000000000..c833cdf9f934b08fc449da077efae44aa9a06f18 GIT binary patch literal 990 zcmah{&2G~`5Ozt6#BxUB0BGt|a)MeHBzh^WP)i*V6_O}*5D+43W3QVP_S)KAHz`pi zcm>WJc^aOC$6(e@N~!o+%den-3;)Sr%Y!Z0+w(d{LAMq3-uf@P z9m3N*lNvI$JbmPO%o9e4pea*14H@ji{DR<>2{Yf&&6LZ;8JC$DI=^T*Ba%xlbR%}U zITKu*!h8w30IGn(BDw1{$&~BKrT>oSEll57hHpZeMQq=ZPSXIfv;d*Is6d=aFi`;) zaRyv0|GCCbxYCWL2?L0zrbu-GbtR)wnZ5)z7h1BgVd6I7ve+xfDrk(z4*+%OisT#$ zRkbMQ68mL{7!IasRE86N#$2)x!D-R6OmfXY6zLc{TyYHxO~(n_b*@}Av|9(SyZyHB z1)agGL$7a-nuOHrbvUS!;zZ!62(4hslf;Y(%~9cqML=USJ$k}b$;JhQk>6X~JFcw~ z%d9)^A9mZpvl9=`=Dly-vourMXb_;{MX9UeQ7N~ZpAY<7R_*(II{NZyIx1$jt(Dau zHOneZ9ejjVI+sG|%rH|rlgP`o7b`AXUNIxrip1vZklyjijR&>AvAb(Xm+RYSv;Bwb WTE+Dm&))IcO#@!RC&c}$ajc&zD=F;& literal 0 HcmV?d00001 diff --git a/cpython-tests/cp311/ziptestdata/header.sh b/cpython-tests/cp311/ziptestdata/header.sh new file mode 100644 index 0000000..52dc91a --- /dev/null +++ b/cpython-tests/cp311/ziptestdata/header.sh @@ -0,0 +1,24 @@ +#!/bin/bash +INTERPRETER_UNDER_TEST="$1" +if [[ ! -x "${INTERPRETER_UNDER_TEST}" ]]; then + echo "Interpreter must be the command line argument." + exit 4 +fi +EXECUTABLE="$0" exec "${INTERPRETER_UNDER_TEST}" -E - <LV!1JBS5x8fC7-R0%8=FkcTh5&DbW=z*2 uYc>UI2Du%m9b!Lz?W_zi?FI&&mdJ6*Ca-0n$rA90maKRyZ91 literal 0 HcmV?d00001 diff --git a/cpython-test/cp312/zipdir_backslash.zip b/cpython-tests/cp312/zipdir_backslash.zip similarity index 100% rename from cpython-test/cp312/zipdir_backslash.zip rename to cpython-tests/cp312/zipdir_backslash.zip diff --git a/cpython-test/cp312/ziptestdata/README.md b/cpython-tests/cp312/ziptestdata/README.md similarity index 100% rename from cpython-test/cp312/ziptestdata/README.md rename to cpython-tests/cp312/ziptestdata/README.md diff --git a/cpython-tests/cp312/ziptestdata/exe_with_z64 b/cpython-tests/cp312/ziptestdata/exe_with_z64 new file mode 100644 index 0000000000000000000000000000000000000000..82b03cf39d919d9de05866b4f7cdf1dfe47e6eb3 GIT binary patch literal 978 zcmaJ<-EY${5KkEl$xl4ME4rnV(xNp330}q~w6RoFg|_N41e7XrQ)jhEoXByzLbV6} z7bN~nxXZ`b5aOcPclPh+`)uy)&!pO)@qEF01K%5u#vZQ0`QQ{+-#hbQJlMzfN zumhbn*t?s5Bd=_jPG5pq2*m(Jgo_mHo-#sbTHp%FGB+?21c5M360YVDOC^Boi)A8| zaqW`1mIj`)NHXt(_xjvFK6&e598YZ!YZ3l8f{q6rI6U+Qr@^orj6V8rh65&(EY$|m zyw<+SERwNcOz}kI84m>G zSHN@NP(AKCZFVWm;@bWsvo1d0s^NQ(q;qlPXs1m?Of5j_0ahSNH4rM0DoR1B`pzXg zmbq!Q2?j9dhGVD|)zyN}i{}esyQ-xKTZG$#>tt`JC1{4sF91y#s`x7`^Rh*e)YvZy zgkqqkaUCw?O1P{lg45-zR7)d3Et45`xQsPi8a|7~fpf#r#O@xyAC7yz7Yxqdop@t= z+GecTY{GH*C{6^9iZVG|$~dMm;TcwVF6O`^njW)|c@d2ZNMpBKJnC=V?N}s_K0g`u zf>&q1Drr~`txm&wV0p#0b-g#i7nomB!y-wOlGog%8hujhFq@*CrC0V>0$BJLY}9Yu zdAxPoGdZHaQ8}dT$9GygqyF~x9%(2wjr1B?@B4I!vMx6ZdG|^ES=ode_3v$y*}#wR GCH6PMMGy`E literal 0 HcmV?d00001 diff --git a/cpython-tests/cp312/ziptestdata/exe_with_zip b/cpython-tests/cp312/ziptestdata/exe_with_zip new file mode 100644 index 0000000000000000000000000000000000000000..c833cdf9f934b08fc449da077efae44aa9a06f18 GIT binary patch literal 990 zcmah{&2G~`5Ozt6#BxUB0BGt|a)MeHBzh^WP)i*V6_O}*5D+43W3QVP_S)KAHz`pi zcm>WJc^aOC$6(e@N~!o+%den-3;)Sr%Y!Z0+w(d{LAMq3-uf@P z9m3N*lNvI$JbmPO%o9e4pea*14H@ji{DR<>2{Yf&&6LZ;8JC$DI=^T*Ba%xlbR%}U zITKu*!h8w30IGn(BDw1{$&~BKrT>oSEll57hHpZeMQq=ZPSXIfv;d*Is6d=aFi`;) zaRyv0|GCCbxYCWL2?L0zrbu-GbtR)wnZ5)z7h1BgVd6I7ve+xfDrk(z4*+%OisT#$ zRkbMQ68mL{7!IasRE86N#$2)x!D-R6OmfXY6zLc{TyYHxO~(n_b*@}Av|9(SyZyHB z1)agGL$7a-nuOHrbvUS!;zZ!62(4hslf;Y(%~9cqML=USJ$k}b$;JhQk>6X~JFcw~ z%d9)^A9mZpvl9=`=Dly-vourMXb_;{MX9UeQ7N~ZpAY<7R_*(II{NZyIx1$jt(Dau zHOneZ9ejjVI+sG|%rH|rlgP`o7b`AXUNIxrip1vZklyjijR&>AvAb(Xm+RYSv;Bwb WTE+Dm&))IcO#@!RC&c}$ajc&zD=F;& literal 0 HcmV?d00001 diff --git a/cpython-tests/cp312/ziptestdata/header.sh b/cpython-tests/cp312/ziptestdata/header.sh new file mode 100644 index 0000000..52dc91a --- /dev/null +++ b/cpython-tests/cp312/ziptestdata/header.sh @@ -0,0 +1,24 @@ +#!/bin/bash +INTERPRETER_UNDER_TEST="$1" +if [[ ! -x "${INTERPRETER_UNDER_TEST}" ]]; then + echo "Interpreter must be the command line argument." + exit 4 +fi +EXECUTABLE="$0" exec "${INTERPRETER_UNDER_TEST}" -E - <e|f"g?h*i', ','), r'a\b,c_d_e_f_g_h_i') + self.assertEqual(san('../../foo../../ba..r', '/'), r'foo/ba..r') + + def test_extract_hackers_arcnames_common_cases(self): + common_hacknames = [ + ('../foo/bar', 'foo/bar'), + ('foo/../bar', 'foo/bar'), + ('foo/../../bar', 'foo/bar'), + ('foo/bar/..', 'foo/bar'), + ('./../foo/bar', 'foo/bar'), + ('/foo/bar', 'foo/bar'), + ('/foo/../bar', 'foo/bar'), + ('/foo/../../bar', 'foo/bar'), + ] + self._test_extract_hackers_arcnames(common_hacknames) + + @unittest.skipIf(os.path.sep != '\\', 'Requires \\ as path separator.') + def test_extract_hackers_arcnames_windows_only(self): + """Test combination of path fixing and windows name sanitization.""" + windows_hacknames = [ + (r'..\foo\bar', 'foo/bar'), + (r'..\/foo\/bar', 'foo/bar'), + (r'foo/\..\/bar', 'foo/bar'), + (r'foo\/../\bar', 'foo/bar'), + (r'C:foo/bar', 'foo/bar'), + (r'C:/foo/bar', 'foo/bar'), + (r'C://foo/bar', 'foo/bar'), + (r'C:\foo\bar', 'foo/bar'), + (r'//conky/mountpoint/foo/bar', 'foo/bar'), + (r'\\conky\mountpoint\foo\bar', 'foo/bar'), + (r'///conky/mountpoint/foo/bar', 'conky/mountpoint/foo/bar'), + (r'\\\conky\mountpoint\foo\bar', 'conky/mountpoint/foo/bar'), + (r'//conky//mountpoint/foo/bar', 'conky/mountpoint/foo/bar'), + (r'\\conky\\mountpoint\foo\bar', 'conky/mountpoint/foo/bar'), + (r'//?/C:/foo/bar', 'foo/bar'), + (r'\\?\C:\foo\bar', 'foo/bar'), + (r'C:/../C:/foo/bar', 'C_/foo/bar'), + (r'a:b\ce|f"g?h*i', 'b/c_d_e_f_g_h_i'), + ('../../foo../../ba..r', 'foo/ba..r'), + ] + self._test_extract_hackers_arcnames(windows_hacknames) + + @unittest.skipIf(os.path.sep != '/', r'Requires / as path separator.') + def test_extract_hackers_arcnames_posix_only(self): + posix_hacknames = [ + ('//foo/bar', 'foo/bar'), + ('../../foo../../ba..r', 'foo../ba..r'), + (r'foo/..\bar', r'foo/..\bar'), + ] + self._test_extract_hackers_arcnames(posix_hacknames) + + def _test_extract_hackers_arcnames(self, hacknames): + for arcname, fixedname in hacknames: + content = b'foobar' + arcname.encode() + with zipfile.ZipFile(TESTFN2, 'w', zipfile.ZIP_STORED) as zipfp: + zinfo = zipfile.ZipInfo() + # preserve backslashes + zinfo.filename = arcname + zinfo.external_attr = 0o600 << 16 + zipfp.writestr(zinfo, content) + + arcname = arcname.replace(os.sep, "/") + targetpath = os.path.join('target', 'subdir', 'subsub') + correctfile = os.path.join(targetpath, *fixedname.split('/')) + + with zipfile.ZipFile(TESTFN2, 'r') as zipfp: + writtenfile = zipfp.extract(arcname, targetpath) + self.assertEqual(writtenfile, correctfile, + msg='extract %r: %r != %r' % + (arcname, writtenfile, correctfile)) + self.check_file(correctfile, content) + rmtree('target') + + with zipfile.ZipFile(TESTFN2, 'r') as zipfp: + zipfp.extractall(targetpath) + self.check_file(correctfile, content) + rmtree('target') + + correctfile = os.path.join(os.getcwd(), *fixedname.split('/')) + + with zipfile.ZipFile(TESTFN2, 'r') as zipfp: + writtenfile = zipfp.extract(arcname) + self.assertEqual(writtenfile, correctfile, + msg="extract %r" % arcname) + self.check_file(correctfile, content) + rmtree(fixedname.split('/')[0]) + + with zipfile.ZipFile(TESTFN2, 'r') as zipfp: + zipfp.extractall() + self.check_file(correctfile, content) + rmtree(fixedname.split('/')[0]) + + unlink(TESTFN2) + + +class OtherTests(unittest.TestCase): + def test_open_via_zip_info(self): + # Create the ZIP archive + with zipfile.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED) as zipfp: + zipfp.writestr("name", "foo") + with self.assertWarns(UserWarning): + zipfp.writestr("name", "bar") + self.assertEqual(zipfp.namelist(), ["name"] * 2) + + with zipfile.ZipFile(TESTFN2, "r") as zipfp: + infos = zipfp.infolist() + data = b"" + for info in infos: + with zipfp.open(info) as zipopen: + data += zipopen.read() + self.assertIn(data, {b"foobar", b"barfoo"}) + data = b"" + for info in infos: + data += zipfp.read(info) + self.assertIn(data, {b"foobar", b"barfoo"}) + + def test_writestr_extended_local_header_issue1202(self): + with zipfile.ZipFile(TESTFN2, 'w') as orig_zip: + for data in 'abcdefghijklmnop': + zinfo = zipfile.ZipInfo(data) + zinfo.flag_bits |= 0x08 # Include an extended local header. + orig_zip.writestr(zinfo, data) + + def test_close(self): + """Check that the zipfile is closed after the 'with' block.""" + with zipfile.ZipFile(TESTFN2, "w") as zipfp: + for fpath, fdata in SMALL_TEST_DATA: + zipfp.writestr(fpath, fdata) + self.assertIsNotNone(zipfp.fp, 'zipfp is not open') + self.assertIsNone(zipfp.fp, 'zipfp is not closed') + + with zipfile.ZipFile(TESTFN2, "r") as zipfp: + self.assertIsNotNone(zipfp.fp, 'zipfp is not open') + self.assertIsNone(zipfp.fp, 'zipfp is not closed') + + def test_close_on_exception(self): + """Check that the zipfile is closed if an exception is raised in the + 'with' block.""" + with zipfile.ZipFile(TESTFN2, "w") as zipfp: + for fpath, fdata in SMALL_TEST_DATA: + zipfp.writestr(fpath, fdata) + + try: + with zipfile.ZipFile(TESTFN2, "r") as zipfp2: + raise zipfile.BadZipFile() + except zipfile.BadZipFile: + self.assertIsNone(zipfp2.fp, 'zipfp is not closed') + + def test_unsupported_version(self): + # File has an extract_version of 120 + data = (b'PK\x03\x04x\x00\x00\x00\x00\x00!p\xa1@\x00\x00\x00\x00\x00\x00' + b'\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00xPK\x01\x02x\x03x\x00\x00\x00\x00' + b'\x00!p\xa1@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00' + b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x01\x00\x00\x00\x00xPK\x05\x06' + b'\x00\x00\x00\x00\x01\x00\x01\x00/\x00\x00\x00\x1f\x00\x00\x00\x00\x00') + + self.assertRaises(NotImplementedError, zipfile.ZipFile, + io.BytesIO(data), 'r') + + @requires_zlib + def test_read_unicode_filenames(self): + # bug #10801 + fname = findfile('zip_cp437_header.zip') + with zipfile.ZipFile(fname) as zipfp: + for name in zipfp.namelist(): + zipfp.open(name).close() + + def test_write_unicode_filenames(self): + with zipfile.ZipFile(TESTFN, "w") as zf: + zf.writestr("foo.txt", "Test for unicode filename") + zf.writestr("\xf6.txt", "Test for unicode filename") + self.assertIsInstance(zf.infolist()[0].filename, str) + + with zipfile.ZipFile(TESTFN, "r") as zf: + self.assertEqual(zf.filelist[0].filename, "foo.txt") + self.assertEqual(zf.filelist[1].filename, "\xf6.txt") + + def test_read_after_write_unicode_filenames(self): + with zipfile.ZipFile(TESTFN2, 'w') as zipfp: + zipfp.writestr('приклад', b'sample') + self.assertEqual(zipfp.read('приклад'), b'sample') + + def test_exclusive_create_zip_file(self): + """Test exclusive creating a new zipfile.""" + unlink(TESTFN2) + filename = 'testfile.txt' + content = b'hello, world. this is some content.' + with zipfile.ZipFile(TESTFN2, "x", zipfile.ZIP_STORED) as zipfp: + zipfp.writestr(filename, content) + with self.assertRaises(FileExistsError): + zipfile.ZipFile(TESTFN2, "x", zipfile.ZIP_STORED) + with zipfile.ZipFile(TESTFN2, "r") as zipfp: + self.assertEqual(zipfp.namelist(), [filename]) + self.assertEqual(zipfp.read(filename), content) + + def test_create_non_existent_file_for_append(self): + if os.path.exists(TESTFN): + os.unlink(TESTFN) + + filename = 'testfile.txt' + content = b'hello, world. this is some content.' + + try: + with zipfile.ZipFile(TESTFN, 'a') as zf: + zf.writestr(filename, content) + except OSError: + self.fail('Could not append data to a non-existent zip file.') + + self.assertTrue(os.path.exists(TESTFN)) + + with zipfile.ZipFile(TESTFN, 'r') as zf: + self.assertEqual(zf.read(filename), content) + + def test_close_erroneous_file(self): + # This test checks that the ZipFile constructor closes the file object + # it opens if there's an error in the file. If it doesn't, the + # traceback holds a reference to the ZipFile object and, indirectly, + # the file object. + # On Windows, this causes the os.unlink() call to fail because the + # underlying file is still open. This is SF bug #412214. + # + with open(TESTFN, "w") as fp: + fp.write("this is not a legal zip file\n") + try: + zf = zipfile.ZipFile(TESTFN) + except zipfile.BadZipFile: + pass + + def test_is_zip_erroneous_file(self): + """Check that is_zipfile() correctly identifies non-zip files.""" + # - passing a filename + with open(TESTFN, "w") as fp: + fp.write("this is not a legal zip file\n") + self.assertFalse(zipfile.is_zipfile(TESTFN)) + # - passing a path-like object + self.assertFalse(zipfile.is_zipfile(pathlib.Path(TESTFN))) + # - passing a file object + with open(TESTFN, "rb") as fp: + self.assertFalse(zipfile.is_zipfile(fp)) + # - passing a file-like object + fp = io.BytesIO() + fp.write(b"this is not a legal zip file\n") + self.assertFalse(zipfile.is_zipfile(fp)) + fp.seek(0, 0) + self.assertFalse(zipfile.is_zipfile(fp)) + + def test_damaged_zipfile(self): + """Check that zipfiles with missing bytes at the end raise BadZipFile.""" + # - Create a valid zip file + fp = io.BytesIO() + with zipfile.ZipFile(fp, mode="w") as zipf: + zipf.writestr("foo.txt", b"O, for a Muse of Fire!") + zipfiledata = fp.getvalue() + + # - Now create copies of it missing the last N bytes and make sure + # a BadZipFile exception is raised when we try to open it + for N in range(len(zipfiledata)): + fp = io.BytesIO(zipfiledata[:N]) + self.assertRaises(zipfile.BadZipFile, zipfile.ZipFile, fp) + + def test_is_zip_valid_file(self): + """Check that is_zipfile() correctly identifies zip files.""" + # - passing a filename + with zipfile.ZipFile(TESTFN, mode="w") as zipf: + zipf.writestr("foo.txt", b"O, for a Muse of Fire!") + + self.assertTrue(zipfile.is_zipfile(TESTFN)) + # - passing a file object + with open(TESTFN, "rb") as fp: + self.assertTrue(zipfile.is_zipfile(fp)) + fp.seek(0, 0) + zip_contents = fp.read() + # - passing a file-like object + fp = io.BytesIO() + fp.write(zip_contents) + self.assertTrue(zipfile.is_zipfile(fp)) + fp.seek(0, 0) + self.assertTrue(zipfile.is_zipfile(fp)) + + def test_non_existent_file_raises_OSError(self): + # make sure we don't raise an AttributeError when a partially-constructed + # ZipFile instance is finalized; this tests for regression on SF tracker + # bug #403871. + + # The bug we're testing for caused an AttributeError to be raised + # when a ZipFile instance was created for a file that did not + # exist; the .fp member was not initialized but was needed by the + # __del__() method. Since the AttributeError is in the __del__(), + # it is ignored, but the user should be sufficiently annoyed by + # the message on the output that regression will be noticed + # quickly. + self.assertRaises(OSError, zipfile.ZipFile, TESTFN) + + def test_empty_file_raises_BadZipFile(self): + f = open(TESTFN, 'w') + f.close() + self.assertRaises(zipfile.BadZipFile, zipfile.ZipFile, TESTFN) + + with open(TESTFN, 'w') as fp: + fp.write("short file") + self.assertRaises(zipfile.BadZipFile, zipfile.ZipFile, TESTFN) + + def test_closed_zip_raises_ValueError(self): + """Verify that testzip() doesn't swallow inappropriate exceptions.""" + data = io.BytesIO() + with zipfile.ZipFile(data, mode="w") as zipf: + zipf.writestr("foo.txt", "O, for a Muse of Fire!") + + # This is correct; calling .read on a closed ZipFile should raise + # a ValueError, and so should calling .testzip. An earlier + # version of .testzip would swallow this exception (and any other) + # and report that the first file in the archive was corrupt. + self.assertRaises(ValueError, zipf.read, "foo.txt") + self.assertRaises(ValueError, zipf.open, "foo.txt") + self.assertRaises(ValueError, zipf.testzip) + self.assertRaises(ValueError, zipf.writestr, "bogus.txt", "bogus") + with open(TESTFN, 'w') as f: + f.write('zipfile test data') + self.assertRaises(ValueError, zipf.write, TESTFN) + + def test_bad_constructor_mode(self): + """Check that bad modes passed to ZipFile constructor are caught.""" + self.assertRaises(ValueError, zipfile.ZipFile, TESTFN, "q") + + def test_bad_open_mode(self): + """Check that bad modes passed to ZipFile.open are caught.""" + with zipfile.ZipFile(TESTFN, mode="w") as zipf: + zipf.writestr("foo.txt", "O, for a Muse of Fire!") + + with zipfile.ZipFile(TESTFN, mode="r") as zipf: + # read the data to make sure the file is there + zipf.read("foo.txt") + self.assertRaises(ValueError, zipf.open, "foo.txt", "q") + # universal newlines support is removed + self.assertRaises(ValueError, zipf.open, "foo.txt", "U") + self.assertRaises(ValueError, zipf.open, "foo.txt", "rU") + + def test_read0(self): + """Check that calling read(0) on a ZipExtFile object returns an empty + string and doesn't advance file pointer.""" + with zipfile.ZipFile(TESTFN, mode="w") as zipf: + zipf.writestr("foo.txt", "O, for a Muse of Fire!") + # read the data to make sure the file is there + with zipf.open("foo.txt") as f: + for i in range(FIXEDTEST_SIZE): + self.assertEqual(f.read(0), b'') + + self.assertEqual(f.read(), b"O, for a Muse of Fire!") + + def test_open_non_existent_item(self): + """Check that attempting to call open() for an item that doesn't + exist in the archive raises a RuntimeError.""" + with zipfile.ZipFile(TESTFN, mode="w") as zipf: + self.assertRaises(KeyError, zipf.open, "foo.txt", "r") + + def test_bad_compression_mode(self): + """Check that bad compression methods passed to ZipFile.open are + caught.""" + self.assertRaises(NotImplementedError, zipfile.ZipFile, TESTFN, "w", -1) + + def test_unsupported_compression(self): + # data is declared as shrunk, but actually deflated + data = (b'PK\x03\x04.\x00\x00\x00\x01\x00\xe4C\xa1@\x00\x00\x00' + b'\x00\x02\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00x\x03\x00PK\x01' + b'\x02.\x03.\x00\x00\x00\x01\x00\xe4C\xa1@\x00\x00\x00\x00\x02\x00\x00' + b'\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + b'\x80\x01\x00\x00\x00\x00xPK\x05\x06\x00\x00\x00\x00\x01\x00\x01\x00' + b'/\x00\x00\x00!\x00\x00\x00\x00\x00') + with zipfile.ZipFile(io.BytesIO(data), 'r') as zipf: + self.assertRaises(NotImplementedError, zipf.open, 'x') + + def test_null_byte_in_filename(self): + """Check that a filename containing a null byte is properly + terminated.""" + with zipfile.ZipFile(TESTFN, mode="w") as zipf: + zipf.writestr("foo.txt\x00qqq", b"O, for a Muse of Fire!") + self.assertEqual(zipf.namelist(), ['foo.txt']) + + def test_struct_sizes(self): + """Check that ZIP internal structure sizes are calculated correctly.""" + self.assertEqual(zipfile.sizeEndCentDir, 22) + self.assertEqual(zipfile.sizeCentralDir, 46) + self.assertEqual(zipfile.sizeEndCentDir64, 56) + self.assertEqual(zipfile.sizeEndCentDir64Locator, 20) + + def test_comments(self): + """Check that comments on the archive are handled properly.""" + + # check default comment is empty + with zipfile.ZipFile(TESTFN, mode="w") as zipf: + self.assertEqual(zipf.comment, b'') + zipf.writestr("foo.txt", "O, for a Muse of Fire!") + + with zipfile.ZipFile(TESTFN, mode="r") as zipfr: + self.assertEqual(zipfr.comment, b'') + + # check a simple short comment + comment = b'Bravely taking to his feet, he beat a very brave retreat.' + with zipfile.ZipFile(TESTFN, mode="w") as zipf: + zipf.comment = comment + zipf.writestr("foo.txt", "O, for a Muse of Fire!") + with zipfile.ZipFile(TESTFN, mode="r") as zipfr: + self.assertEqual(zipf.comment, comment) + + # check a comment of max length + comment2 = ''.join(['%d' % (i**3 % 10) for i in range((1 << 16)-1)]) + comment2 = comment2.encode("ascii") + with zipfile.ZipFile(TESTFN, mode="w") as zipf: + zipf.comment = comment2 + zipf.writestr("foo.txt", "O, for a Muse of Fire!") + + with zipfile.ZipFile(TESTFN, mode="r") as zipfr: + self.assertEqual(zipfr.comment, comment2) + + # check a comment that is too long is truncated + with zipfile.ZipFile(TESTFN, mode="w") as zipf: + with self.assertWarns(UserWarning): + zipf.comment = comment2 + b'oops' + zipf.writestr("foo.txt", "O, for a Muse of Fire!") + with zipfile.ZipFile(TESTFN, mode="r") as zipfr: + self.assertEqual(zipfr.comment, comment2) + + # check that comments are correctly modified in append mode + with zipfile.ZipFile(TESTFN,mode="w") as zipf: + zipf.comment = b"original comment" + zipf.writestr("foo.txt", "O, for a Muse of Fire!") + with zipfile.ZipFile(TESTFN,mode="a") as zipf: + zipf.comment = b"an updated comment" + with zipfile.ZipFile(TESTFN,mode="r") as zipf: + self.assertEqual(zipf.comment, b"an updated comment") + + # check that comments are correctly shortened in append mode + # and the file is indeed truncated + with zipfile.ZipFile(TESTFN,mode="w") as zipf: + zipf.comment = b"original comment that's longer" + zipf.writestr("foo.txt", "O, for a Muse of Fire!") + original_zip_size = os.path.getsize(TESTFN) + with zipfile.ZipFile(TESTFN,mode="a") as zipf: + zipf.comment = b"shorter comment" + self.assertTrue(original_zip_size > os.path.getsize(TESTFN)) + with zipfile.ZipFile(TESTFN,mode="r") as zipf: + self.assertEqual(zipf.comment, b"shorter comment") + + def test_unicode_comment(self): + with zipfile.ZipFile(TESTFN, "w", zipfile.ZIP_STORED) as zipf: + zipf.writestr("foo.txt", "O, for a Muse of Fire!") + with self.assertRaises(TypeError): + zipf.comment = "this is an error" + + def test_change_comment_in_empty_archive(self): + with zipfile.ZipFile(TESTFN, "a", zipfile.ZIP_STORED) as zipf: + self.assertFalse(zipf.filelist) + zipf.comment = b"this is a comment" + with zipfile.ZipFile(TESTFN, "r") as zipf: + self.assertEqual(zipf.comment, b"this is a comment") + + def test_change_comment_in_nonempty_archive(self): + with zipfile.ZipFile(TESTFN, "w", zipfile.ZIP_STORED) as zipf: + zipf.writestr("foo.txt", "O, for a Muse of Fire!") + with zipfile.ZipFile(TESTFN, "a", zipfile.ZIP_STORED) as zipf: + self.assertTrue(zipf.filelist) + zipf.comment = b"this is a comment" + with zipfile.ZipFile(TESTFN, "r") as zipf: + self.assertEqual(zipf.comment, b"this is a comment") + + def test_empty_zipfile(self): + # Check that creating a file in 'w' or 'a' mode and closing without + # adding any files to the archives creates a valid empty ZIP file + zipf = zipfile.ZipFile(TESTFN, mode="w") + zipf.close() + try: + zipf = zipfile.ZipFile(TESTFN, mode="r") + except zipfile.BadZipFile: + self.fail("Unable to create empty ZIP file in 'w' mode") + + zipf = zipfile.ZipFile(TESTFN, mode="a") + zipf.close() + try: + zipf = zipfile.ZipFile(TESTFN, mode="r") + except: + self.fail("Unable to create empty ZIP file in 'a' mode") + + def test_open_empty_file(self): + # Issue 1710703: Check that opening a file with less than 22 bytes + # raises a BadZipFile exception (rather than the previously unhelpful + # OSError) + f = open(TESTFN, 'w') + f.close() + self.assertRaises(zipfile.BadZipFile, zipfile.ZipFile, TESTFN, 'r') + + def test_create_zipinfo_before_1980(self): + self.assertRaises(ValueError, + zipfile.ZipInfo, 'seventies', (1979, 1, 1, 0, 0, 0)) + + def test_zipfile_with_short_extra_field(self): + """If an extra field in the header is less than 4 bytes, skip it.""" + zipdata = ( + b'PK\x03\x04\x14\x00\x00\x00\x00\x00\x93\x9b\xad@\x8b\x9e' + b'\xd9\xd3\x01\x00\x00\x00\x01\x00\x00\x00\x03\x00\x03\x00ab' + b'c\x00\x00\x00APK\x01\x02\x14\x03\x14\x00\x00\x00\x00' + b'\x00\x93\x9b\xad@\x8b\x9e\xd9\xd3\x01\x00\x00\x00\x01\x00\x00' + b'\x00\x03\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x00' + b'\x00\x00\x00abc\x00\x00PK\x05\x06\x00\x00\x00\x00' + b'\x01\x00\x01\x003\x00\x00\x00%\x00\x00\x00\x00\x00' + ) + with zipfile.ZipFile(io.BytesIO(zipdata), 'r') as zipf: + # testzip returns the name of the first corrupt file, or None + self.assertIsNone(zipf.testzip()) + + def test_open_conflicting_handles(self): + # It's only possible to open one writable file handle at a time + msg1 = b"It's fun to charter an accountant!" + msg2 = b"And sail the wide accountant sea" + msg3 = b"To find, explore the funds offshore" + with zipfile.ZipFile(TESTFN2, 'w', zipfile.ZIP_STORED) as zipf: + with zipf.open('foo', mode='w') as w2: + w2.write(msg1) + with zipf.open('bar', mode='w') as w1: + with self.assertRaises(ValueError): + zipf.open('handle', mode='w') + with self.assertRaises(ValueError): + zipf.open('foo', mode='r') + with self.assertRaises(ValueError): + zipf.writestr('str', 'abcde') + with self.assertRaises(ValueError): + zipf.write(__file__, 'file') + with self.assertRaises(ValueError): + zipf.close() + w1.write(msg2) + with zipf.open('baz', mode='w') as w2: + w2.write(msg3) + + with zipfile.ZipFile(TESTFN2, 'r') as zipf: + self.assertEqual(zipf.read('foo'), msg1) + self.assertEqual(zipf.read('bar'), msg2) + self.assertEqual(zipf.read('baz'), msg3) + self.assertEqual(zipf.namelist(), ['foo', 'bar', 'baz']) + + def test_seek_tell(self): + # Test seek functionality + txt = b"Where's Bruce?" + bloc = txt.find(b"Bruce") + # Check seek on a file + with zipfile.ZipFile(TESTFN, "w") as zipf: + zipf.writestr("foo.txt", txt) + with zipfile.ZipFile(TESTFN, "r") as zipf: + with zipf.open("foo.txt", "r") as fp: + fp.seek(bloc, os.SEEK_SET) + self.assertEqual(fp.tell(), bloc) + fp.seek(-bloc, os.SEEK_CUR) + self.assertEqual(fp.tell(), 0) + fp.seek(bloc, os.SEEK_CUR) + self.assertEqual(fp.tell(), bloc) + self.assertEqual(fp.read(5), txt[bloc:bloc+5]) + fp.seek(0, os.SEEK_END) + self.assertEqual(fp.tell(), len(txt)) + fp.seek(0, os.SEEK_SET) + self.assertEqual(fp.tell(), 0) + # Check seek on memory file + data = io.BytesIO() + with zipfile.ZipFile(data, mode="w") as zipf: + zipf.writestr("foo.txt", txt) + with zipfile.ZipFile(data, mode="r") as zipf: + with zipf.open("foo.txt", "r") as fp: + fp.seek(bloc, os.SEEK_SET) + self.assertEqual(fp.tell(), bloc) + fp.seek(-bloc, os.SEEK_CUR) + self.assertEqual(fp.tell(), 0) + fp.seek(bloc, os.SEEK_CUR) + self.assertEqual(fp.tell(), bloc) + self.assertEqual(fp.read(5), txt[bloc:bloc+5]) + fp.seek(0, os.SEEK_END) + self.assertEqual(fp.tell(), len(txt)) + fp.seek(0, os.SEEK_SET) + self.assertEqual(fp.tell(), 0) + + @requires_bz2 + def test_decompress_without_3rd_party_library(self): + data = b'PK\x05\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + zip_file = io.BytesIO(data) + with zipfile.ZipFile(zip_file, 'w', compression=zipfile.ZIP_BZIP2) as zf: + zf.writestr('a.txt', b'a') + with mock.patch('zipfile.bz2', None): + with zipfile.ZipFile(zip_file) as zf: + self.assertRaises(RuntimeError, zf.extract, 'a.txt') + + @requires_zlib + def test_full_overlap(self): + data = ( + b'PK\x03\x04\x14\x00\x00\x00\x08\x00\xa0lH\x05\xe2\x1e' + b'8\xbb\x10\x00\x00\x00\t\x04\x00\x00\x01\x00\x00\x00a\xed' + b'\xc0\x81\x08\x00\x00\x00\xc00\xd6\xfbK\\d\x0b`P' + b'K\x01\x02\x14\x00\x14\x00\x00\x00\x08\x00\xa0lH\x05\xe2' + b'\x1e8\xbb\x10\x00\x00\x00\t\x04\x00\x00\x01\x00\x00\x00\x00' + b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00aPK' + b'\x01\x02\x14\x00\x14\x00\x00\x00\x08\x00\xa0lH\x05\xe2\x1e' + b'8\xbb\x10\x00\x00\x00\t\x04\x00\x00\x01\x00\x00\x00\x00\x00' + b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00bPK\x05' + b'\x06\x00\x00\x00\x00\x02\x00\x02\x00^\x00\x00\x00/\x00\x00' + b'\x00\x00\x00' + ) + with zipfile.ZipFile(io.BytesIO(data), 'r') as zipf: + self.assertEqual(zipf.namelist(), ['a', 'b']) + zi = zipf.getinfo('a') + self.assertEqual(zi.header_offset, 0) + self.assertEqual(zi.compress_size, 16) + self.assertEqual(zi.file_size, 1033) + zi = zipf.getinfo('b') + self.assertEqual(zi.header_offset, 0) + self.assertEqual(zi.compress_size, 16) + self.assertEqual(zi.file_size, 1033) + self.assertEqual(len(zipf.read('a')), 1033) + with self.assertRaisesRegex(zipfile.BadZipFile, 'File name.*differ'): + zipf.read('b') + + @requires_zlib + def test_quoted_overlap(self): + data = ( + b'PK\x03\x04\x14\x00\x00\x00\x08\x00\xa0lH\x05Y\xfc' + b'8\x044\x00\x00\x00(\x04\x00\x00\x01\x00\x00\x00a\x00' + b'\x1f\x00\xe0\xffPK\x03\x04\x14\x00\x00\x00\x08\x00\xa0l' + b'H\x05\xe2\x1e8\xbb\x10\x00\x00\x00\t\x04\x00\x00\x01\x00' + b'\x00\x00b\xed\xc0\x81\x08\x00\x00\x00\xc00\xd6\xfbK\\' + b'd\x0b`PK\x01\x02\x14\x00\x14\x00\x00\x00\x08\x00\xa0' + b'lH\x05Y\xfc8\x044\x00\x00\x00(\x04\x00\x00\x01' + b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + b'\x00aPK\x01\x02\x14\x00\x14\x00\x00\x00\x08\x00\xa0l' + b'H\x05\xe2\x1e8\xbb\x10\x00\x00\x00\t\x04\x00\x00\x01\x00' + b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00$\x00\x00\x00' + b'bPK\x05\x06\x00\x00\x00\x00\x02\x00\x02\x00^\x00\x00' + b'\x00S\x00\x00\x00\x00\x00' + ) + with zipfile.ZipFile(io.BytesIO(data), 'r') as zipf: + self.assertEqual(zipf.namelist(), ['a', 'b']) + zi = zipf.getinfo('a') + self.assertEqual(zi.header_offset, 0) + self.assertEqual(zi.compress_size, 52) + self.assertEqual(zi.file_size, 1064) + zi = zipf.getinfo('b') + self.assertEqual(zi.header_offset, 36) + self.assertEqual(zi.compress_size, 16) + self.assertEqual(zi.file_size, 1033) + with self.assertRaisesRegex(zipfile.BadZipFile, 'Overlapped entries'): + zipf.read('a') + self.assertEqual(len(zipf.read('b')), 1033) + + def tearDown(self): + unlink(TESTFN) + unlink(TESTFN2) + + +class AbstractBadCrcTests: + def test_testzip_with_bad_crc(self): + """Tests that files with bad CRCs return their name from testzip.""" + zipdata = self.zip_with_bad_crc + + with zipfile.ZipFile(io.BytesIO(zipdata), mode="r") as zipf: + # testzip returns the name of the first corrupt file, or None + self.assertEqual('afile', zipf.testzip()) + + def test_read_with_bad_crc(self): + """Tests that files with bad CRCs raise a BadZipFile exception when read.""" + zipdata = self.zip_with_bad_crc + + # Using ZipFile.read() + with zipfile.ZipFile(io.BytesIO(zipdata), mode="r") as zipf: + self.assertRaises(zipfile.BadZipFile, zipf.read, 'afile') + + # Using ZipExtFile.read() + with zipfile.ZipFile(io.BytesIO(zipdata), mode="r") as zipf: + with zipf.open('afile', 'r') as corrupt_file: + self.assertRaises(zipfile.BadZipFile, corrupt_file.read) + + # Same with small reads (in order to exercise the buffering logic) + with zipfile.ZipFile(io.BytesIO(zipdata), mode="r") as zipf: + with zipf.open('afile', 'r') as corrupt_file: + corrupt_file.MIN_READ_SIZE = 2 + with self.assertRaises(zipfile.BadZipFile): + while corrupt_file.read(2): + pass + + +class StoredBadCrcTests(AbstractBadCrcTests, unittest.TestCase): + compression = zipfile.ZIP_STORED + zip_with_bad_crc = ( + b'PK\003\004\024\0\0\0\0\0 \213\212;:r' + b'\253\377\f\0\0\0\f\0\0\0\005\0\0\000af' + b'ilehello,AworldP' + b'K\001\002\024\003\024\0\0\0\0\0 \213\212;:' + b'r\253\377\f\0\0\0\f\0\0\0\005\0\0\0\0' + b'\0\0\0\0\0\0\0\200\001\0\0\0\000afi' + b'lePK\005\006\0\0\0\0\001\0\001\0003\000' + b'\0\0/\0\0\0\0\0') + +@requires_zlib +class DeflateBadCrcTests(AbstractBadCrcTests, unittest.TestCase): + compression = zipfile.ZIP_DEFLATED + zip_with_bad_crc = ( + b'PK\x03\x04\x14\x00\x00\x00\x08\x00n}\x0c=FA' + b'KE\x10\x00\x00\x00n\x00\x00\x00\x05\x00\x00\x00af' + b'ile\xcbH\xcd\xc9\xc9W(\xcf/\xcaI\xc9\xa0' + b'=\x13\x00PK\x01\x02\x14\x03\x14\x00\x00\x00\x08\x00n' + b'}\x0c=FAKE\x10\x00\x00\x00n\x00\x00\x00\x05' + b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x01\x00\x00\x00' + b'\x00afilePK\x05\x06\x00\x00\x00\x00\x01\x00' + b'\x01\x003\x00\x00\x003\x00\x00\x00\x00\x00') + +@requires_bz2 +class Bzip2BadCrcTests(AbstractBadCrcTests, unittest.TestCase): + compression = zipfile.ZIP_BZIP2 + zip_with_bad_crc = ( + b'PK\x03\x04\x14\x03\x00\x00\x0c\x00nu\x0c=FA' + b'KE8\x00\x00\x00n\x00\x00\x00\x05\x00\x00\x00af' + b'ileBZh91AY&SY\xd4\xa8\xca' + b'\x7f\x00\x00\x0f\x11\x80@\x00\x06D\x90\x80 \x00 \xa5' + b'P\xd9!\x03\x03\x13\x13\x13\x89\xa9\xa9\xc2u5:\x9f' + b'\x8b\xb9"\x9c(HjTe?\x80PK\x01\x02\x14' + b'\x03\x14\x03\x00\x00\x0c\x00nu\x0c=FAKE8' + b'\x00\x00\x00n\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x00' + b'\x00 \x80\x80\x81\x00\x00\x00\x00afilePK' + b'\x05\x06\x00\x00\x00\x00\x01\x00\x01\x003\x00\x00\x00[\x00' + b'\x00\x00\x00\x00') + +@requires_lzma +class LzmaBadCrcTests(AbstractBadCrcTests, unittest.TestCase): + compression = zipfile.ZIP_LZMA + zip_with_bad_crc = ( + b'PK\x03\x04\x14\x03\x00\x00\x0e\x00nu\x0c=FA' + b'KE\x1b\x00\x00\x00n\x00\x00\x00\x05\x00\x00\x00af' + b'ile\t\x04\x05\x00]\x00\x00\x00\x04\x004\x19I' + b'\xee\x8d\xe9\x17\x89:3`\tq!.8\x00PK' + b'\x01\x02\x14\x03\x14\x03\x00\x00\x0e\x00nu\x0c=FA' + b'KE\x1b\x00\x00\x00n\x00\x00\x00\x05\x00\x00\x00\x00\x00' + b'\x00\x00\x00\x00 \x80\x80\x81\x00\x00\x00\x00afil' + b'ePK\x05\x06\x00\x00\x00\x00\x01\x00\x01\x003\x00\x00' + b'\x00>\x00\x00\x00\x00\x00') + + +class DecryptionTests(unittest.TestCase): + """Check that ZIP decryption works. Since the library does not + support encryption at the moment, we use a pre-generated encrypted + ZIP file.""" + + data = ( + b'PK\x03\x04\x14\x00\x01\x00\x00\x00n\x92i.#y\xef?&\x00\x00\x00\x1a\x00' + b'\x00\x00\x08\x00\x00\x00test.txt\xfa\x10\xa0gly|\xfa-\xc5\xc0=\xf9y' + b'\x18\xe0\xa8r\xb3Z}Lg\xbc\xae\xf9|\x9b\x19\xe4\x8b\xba\xbb)\x8c\xb0\xdbl' + b'PK\x01\x02\x14\x00\x14\x00\x01\x00\x00\x00n\x92i.#y\xef?&\x00\x00\x00' + b'\x1a\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00\x00\x01\x00 \x00\xb6\x81' + b'\x00\x00\x00\x00test.txtPK\x05\x06\x00\x00\x00\x00\x01\x00\x01\x006\x00' + b'\x00\x00L\x00\x00\x00\x00\x00' ) + data2 = ( + b'PK\x03\x04\x14\x00\t\x00\x08\x00\xcf}38xu\xaa\xb2\x14\x00\x00\x00\x00\x02' + b'\x00\x00\x04\x00\x15\x00zeroUT\t\x00\x03\xd6\x8b\x92G\xda\x8b\x92GUx\x04' + b'\x00\xe8\x03\xe8\x03\xc7", "exec") +raise_src = 'def do_raise(): raise TypeError\n' + +def make_pyc(co, mtime, size): + data = marshal.dumps(co) + if type(mtime) is type(0.0): + # Mac mtimes need a bit of special casing + if mtime < 0x7fffffff: + mtime = int(mtime) + else: + mtime = int(-0x100000000 + int(mtime)) + pyc = (importlib.util.MAGIC_NUMBER + + struct.pack("", "exec"), NOW, len(src)) + files = {TESTMOD + pyc_ext: (NOW, pyc), + "some.data": (NOW, "some data")} + self.doTest(pyc_ext, files, TESTMOD) + + def testDefaultOptimizationLevel(self): + # zipimport should use the default optimization level (#28131) + src = """if 1: # indent hack + def test(val): + assert(val) + return val\n""" + files = {TESTMOD + '.py': (NOW, src)} + self.makeZip(files) + sys.path.insert(0, TEMP_ZIP) + mod = importlib.import_module(TESTMOD) + self.assertEqual(mod.test(1), 1) + self.assertRaises(AssertionError, mod.test, False) + + def testImport_WithStuff(self): + # try importing from a zipfile which contains additional + # stuff at the beginning of the file + files = {TESTMOD + ".py": (NOW, test_src)} + self.doTest(".py", files, TESTMOD, + stuff=b"Some Stuff"*31) + + def assertModuleSource(self, module): + self.assertEqual(inspect.getsource(module), test_src) + + def testGetSource(self): + files = {TESTMOD + ".py": (NOW, test_src)} + self.doTest(".py", files, TESTMOD, call=self.assertModuleSource) + + def testGetCompiledSource(self): + pyc = make_pyc(compile(test_src, "", "exec"), NOW, len(test_src)) + files = {TESTMOD + ".py": (NOW, test_src), + TESTMOD + pyc_ext: (NOW, pyc)} + self.doTest(pyc_ext, files, TESTMOD, call=self.assertModuleSource) + + def runDoctest(self, callback): + files = {TESTMOD + ".py": (NOW, test_src), + "xyz.txt": (NOW, ">>> log.append(True)\n")} + self.doTest(".py", files, TESTMOD, call=callback) + + def doDoctestFile(self, module): + log = [] + old_master, doctest.master = doctest.master, None + try: + doctest.testfile( + 'xyz.txt', package=module, module_relative=True, + globs=locals() + ) + finally: + doctest.master = old_master + self.assertEqual(log,[True]) + + def testDoctestFile(self): + self.runDoctest(self.doDoctestFile) + + def doDoctestSuite(self, module): + log = [] + doctest.DocFileTest( + 'xyz.txt', package=module, module_relative=True, + globs=locals() + ).run() + self.assertEqual(log,[True]) + + def testDoctestSuite(self): + self.runDoctest(self.doDoctestSuite) + + def doTraceback(self, module): + try: + module.do_raise() + except: + tb = sys.exc_info()[2].tb_next + + f,lno,n,line = extract_tb(tb, 1)[0] + self.assertEqual(line, raise_src.strip()) + + f,lno,n,line = extract_stack(tb.tb_frame, 1)[0] + self.assertEqual(line, raise_src.strip()) + + s = io.StringIO() + print_tb(tb, 1, s) + self.assertTrue(s.getvalue().endswith(raise_src)) + else: + raise AssertionError("This ought to be impossible") + + def testTraceback(self): + files = {TESTMOD + ".py": (NOW, raise_src)} + self.doTest(None, files, TESTMOD, call=self.doTraceback) + + @unittest.skipIf(support.TESTFN_UNENCODABLE is None, + "need an unencodable filename") + def testUnencodable(self): + filename = support.TESTFN_UNENCODABLE + ".zip" + self.addCleanup(support.unlink, filename) + with ZipFile(filename, "w") as z: + zinfo = ZipInfo(TESTMOD + ".py", time.localtime(NOW)) + zinfo.compress_type = self.compression + z.writestr(zinfo, test_src) + zipimport.zipimporter(filename).load_module(TESTMOD) + + def testBytesPath(self): + filename = support.TESTFN + ".zip" + self.addCleanup(support.unlink, filename) + with ZipFile(filename, "w") as z: + zinfo = ZipInfo(TESTMOD + ".py", time.localtime(NOW)) + zinfo.compress_type = self.compression + z.writestr(zinfo, test_src) + + zipimport.zipimporter(filename) + zipimport.zipimporter(os.fsencode(filename)) + with self.assertRaises(TypeError): + zipimport.zipimporter(bytearray(os.fsencode(filename))) + with self.assertRaises(TypeError): + zipimport.zipimporter(memoryview(os.fsencode(filename))) + + def testComment(self): + files = {TESTMOD + ".py": (NOW, test_src)} + self.doTest(".py", files, TESTMOD, comment=b"comment") + + def testBeginningCruftAndComment(self): + files = {TESTMOD + ".py": (NOW, test_src)} + self.doTest(".py", files, TESTMOD, stuff=b"cruft" * 64, comment=b"hi") + + def testLargestPossibleComment(self): + files = {TESTMOD + ".py": (NOW, test_src)} + self.doTest(".py", files, TESTMOD, comment=b"c" * ((1 << 16) - 1)) + + +@support.requires_zlib +class CompressedZipImportTestCase(UncompressedZipImportTestCase): + compression = ZIP_DEFLATED + + +class BadFileZipImportTestCase(unittest.TestCase): + def assertZipFailure(self, filename): + self.assertRaises(zipimport.ZipImportError, + zipimport.zipimporter, filename) + + def testNoFile(self): + self.assertZipFailure('AdfjdkFJKDFJjdklfjs') + + def testEmptyFilename(self): + self.assertZipFailure('') + + def testBadArgs(self): + self.assertRaises(TypeError, zipimport.zipimporter, None) + self.assertRaises(TypeError, zipimport.zipimporter, TESTMOD, kwd=None) + self.assertRaises(TypeError, zipimport.zipimporter, + list(os.fsencode(TESTMOD))) + + def testFilenameTooLong(self): + self.assertZipFailure('A' * 33000) + + def testEmptyFile(self): + support.unlink(TESTMOD) + support.create_empty_file(TESTMOD) + self.assertZipFailure(TESTMOD) + + def testFileUnreadable(self): + support.unlink(TESTMOD) + fd = os.open(TESTMOD, os.O_CREAT, 000) + try: + os.close(fd) + + with self.assertRaises(zipimport.ZipImportError) as cm: + zipimport.zipimporter(TESTMOD) + finally: + # If we leave "the read-only bit" set on Windows, nothing can + # delete TESTMOD, and later tests suffer bogus failures. + os.chmod(TESTMOD, 0o666) + support.unlink(TESTMOD) + + def testNotZipFile(self): + support.unlink(TESTMOD) + fp = open(TESTMOD, 'w+') + fp.write('a' * 22) + fp.close() + self.assertZipFailure(TESTMOD) + + # XXX: disabled until this works on Big-endian machines + def _testBogusZipFile(self): + support.unlink(TESTMOD) + fp = open(TESTMOD, 'w+') + fp.write(struct.pack('=I', 0x06054B50)) + fp.write('a' * 18) + fp.close() + z = zipimport.zipimporter(TESTMOD) + + try: + self.assertRaises(TypeError, z.find_module, None) + self.assertRaises(TypeError, z.load_module, None) + self.assertRaises(TypeError, z.is_package, None) + self.assertRaises(TypeError, z.get_code, None) + self.assertRaises(TypeError, z.get_data, None) + self.assertRaises(TypeError, z.get_source, None) + + error = zipimport.ZipImportError + self.assertEqual(z.find_module('abc'), None) + + self.assertRaises(error, z.load_module, 'abc') + self.assertRaises(error, z.get_code, 'abc') + self.assertRaises(OSError, z.get_data, 'abc') + self.assertRaises(error, z.get_source, 'abc') + self.assertRaises(error, z.is_package, 'abc') + finally: + zipimport._zip_directory_cache.clear() + + +def test_main(): + try: + support.run_unittest( + UncompressedZipImportTestCase, + CompressedZipImportTestCase, + BadFileZipImportTestCase, + ) + finally: + support.unlink(TESTMOD) + +if __name__ == "__main__": + test_main() diff --git a/cpython-tests/cp38/test_zipimport_support.py b/cpython-tests/cp38/test_zipimport_support.py new file mode 100644 index 0000000..8856101 --- /dev/null +++ b/cpython-tests/cp38/test_zipimport_support.py @@ -0,0 +1,242 @@ +# This test module covers support in various parts of the standard library +# for working with modules located inside zipfiles +# The tests are centralised in this fashion to make it easy to drop them +# if a platform doesn't support zipimport +import test.support +import os +import os.path +import sys +import textwrap +import zipfile +import zipimport +import doctest +import inspect +import linecache +import unittest +from test.support.script_helper import (spawn_python, kill_python, assert_python_ok, + make_script, make_zip_script) + +verbose = test.support.verbose + +# Library modules covered by this test set +# pdb (Issue 4201) +# inspect (Issue 4223) +# doctest (Issue 4197) + +# Other test modules with zipimport related tests +# test_zipimport (of course!) +# test_cmd_line_script (covers the zipimport support in runpy) + +# Retrieve some helpers from other test cases +from test import (test_doctest, sample_doctest, sample_doctest_no_doctests, + sample_doctest_no_docstrings) + + +def _run_object_doctest(obj, module): + finder = doctest.DocTestFinder(verbose=verbose, recurse=False) + runner = doctest.DocTestRunner(verbose=verbose) + # Use the object's fully qualified name if it has one + # Otherwise, use the module's name + try: + name = "%s.%s" % (obj.__module__, obj.__qualname__) + except AttributeError: + name = module.__name__ + for example in finder.find(obj, name, module): + runner.run(example) + f, t = runner.failures, runner.tries + if f: + raise test.support.TestFailed("%d of %d doctests failed" % (f, t)) + if verbose: + print ('doctest (%s) ... %d tests with zero failures' % (module.__name__, t)) + return f, t + + + +class ZipSupportTests(unittest.TestCase): + # This used to use the ImportHooksBaseTestCase to restore + # the state of the import related information + # in the sys module after each test. However, that restores + # *too much* information and breaks for the invocation + # of test_doctest. So we do our own thing and leave + # sys.modules alone. + # We also clear the linecache and zipimport cache + # just to avoid any bogus errors due to name reuse in the tests + def setUp(self): + linecache.clearcache() + zipimport._zip_directory_cache.clear() + self.path = sys.path[:] + self.meta_path = sys.meta_path[:] + self.path_hooks = sys.path_hooks[:] + sys.path_importer_cache.clear() + + def tearDown(self): + sys.path[:] = self.path + sys.meta_path[:] = self.meta_path + sys.path_hooks[:] = self.path_hooks + sys.path_importer_cache.clear() + + def test_inspect_getsource_issue4223(self): + test_src = "def foo(): pass\n" + with test.support.temp_dir() as d: + init_name = make_script(d, '__init__', test_src) + name_in_zip = os.path.join('zip_pkg', + os.path.basename(init_name)) + zip_name, run_name = make_zip_script(d, 'test_zip', + init_name, name_in_zip) + os.remove(init_name) + sys.path.insert(0, zip_name) + import zip_pkg + try: + self.assertEqual(inspect.getsource(zip_pkg.foo), test_src) + finally: + del sys.modules["zip_pkg"] + + def test_doctest_issue4197(self): + # To avoid having to keep two copies of the doctest module's + # unit tests in sync, this test works by taking the source of + # test_doctest itself, rewriting it a bit to cope with a new + # location, and then throwing it in a zip file to make sure + # everything still works correctly + test_src = inspect.getsource(test_doctest) + test_src = test_src.replace( + "from test import test_doctest", + "import test_zipped_doctest as test_doctest") + test_src = test_src.replace("test.test_doctest", + "test_zipped_doctest") + test_src = test_src.replace("test.sample_doctest", + "sample_zipped_doctest") + # The sample doctest files rewritten to include in the zipped version. + sample_sources = {} + for mod in [sample_doctest, sample_doctest_no_doctests, + sample_doctest_no_docstrings]: + src = inspect.getsource(mod) + src = src.replace("test.test_doctest", "test_zipped_doctest") + # Rewrite the module name so that, for example, + # "test.sample_doctest" becomes "sample_zipped_doctest". + mod_name = mod.__name__.split(".")[-1] + mod_name = mod_name.replace("sample_", "sample_zipped_") + sample_sources[mod_name] = src + + with test.support.temp_dir() as d: + script_name = make_script(d, 'test_zipped_doctest', + test_src) + zip_name, run_name = make_zip_script(d, 'test_zip', + script_name) + with zipfile.ZipFile(zip_name, 'a') as z: + for mod_name, src in sample_sources.items(): + z.writestr(mod_name + ".py", src) + if verbose: + with zipfile.ZipFile(zip_name, 'r') as zip_file: + print ('Contents of %r:' % zip_name) + zip_file.printdir() + os.remove(script_name) + sys.path.insert(0, zip_name) + import test_zipped_doctest + try: + # Some of the doc tests depend on the colocated text files + # which aren't available to the zipped version (the doctest + # module currently requires real filenames for non-embedded + # tests). So we're forced to be selective about which tests + # to run. + # doctest could really use some APIs which take a text + # string or a file object instead of a filename... + known_good_tests = [ + test_zipped_doctest.SampleClass, + test_zipped_doctest.SampleClass.NestedClass, + test_zipped_doctest.SampleClass.NestedClass.__init__, + test_zipped_doctest.SampleClass.__init__, + test_zipped_doctest.SampleClass.a_classmethod, + test_zipped_doctest.SampleClass.a_property, + test_zipped_doctest.SampleClass.a_staticmethod, + test_zipped_doctest.SampleClass.double, + test_zipped_doctest.SampleClass.get, + test_zipped_doctest.SampleNewStyleClass, + test_zipped_doctest.SampleNewStyleClass.__init__, + test_zipped_doctest.SampleNewStyleClass.double, + test_zipped_doctest.SampleNewStyleClass.get, + test_zipped_doctest.sample_func, + test_zipped_doctest.test_DocTest, + test_zipped_doctest.test_DocTestParser, + test_zipped_doctest.test_DocTestRunner.basics, + test_zipped_doctest.test_DocTestRunner.exceptions, + test_zipped_doctest.test_DocTestRunner.option_directives, + test_zipped_doctest.test_DocTestRunner.optionflags, + test_zipped_doctest.test_DocTestRunner.verbose_flag, + test_zipped_doctest.test_Example, + test_zipped_doctest.test_debug, + test_zipped_doctest.test_testsource, + test_zipped_doctest.test_trailing_space_in_test, + test_zipped_doctest.test_DocTestSuite, + test_zipped_doctest.test_DocTestFinder, + ] + # These tests are the ones which need access + # to the data files, so we don't run them + fail_due_to_missing_data_files = [ + test_zipped_doctest.test_DocFileSuite, + test_zipped_doctest.test_testfile, + test_zipped_doctest.test_unittest_reportflags, + ] + + for obj in known_good_tests: + _run_object_doctest(obj, test_zipped_doctest) + finally: + del sys.modules["test_zipped_doctest"] + + def test_doctest_main_issue4197(self): + test_src = textwrap.dedent("""\ + class Test: + ">>> 'line 2'" + pass + + import doctest + doctest.testmod() + """) + pattern = 'File "%s", line 2, in %s' + with test.support.temp_dir() as d: + script_name = make_script(d, 'script', test_src) + rc, out, err = assert_python_ok(script_name) + expected = pattern % (script_name, "__main__.Test") + if verbose: + print ("Expected line", expected) + print ("Got stdout:") + print (ascii(out)) + self.assertIn(expected.encode('utf-8'), out) + zip_name, run_name = make_zip_script(d, "test_zip", + script_name, '__main__.py') + rc, out, err = assert_python_ok(zip_name) + expected = pattern % (run_name, "__main__.Test") + if verbose: + print ("Expected line", expected) + print ("Got stdout:") + print (ascii(out)) + self.assertIn(expected.encode('utf-8'), out) + + def test_pdb_issue4201(self): + test_src = textwrap.dedent("""\ + def f(): + pass + + import pdb + pdb.Pdb(nosigint=True).runcall(f) + """) + with test.support.temp_dir() as d: + script_name = make_script(d, 'script', test_src) + p = spawn_python(script_name) + p.stdin.write(b'l\n') + data = kill_python(p) + # bdb/pdb applies normcase to its filename before displaying + self.assertIn(os.path.normcase(script_name.encode('utf-8')), data) + zip_name, run_name = make_zip_script(d, "test_zip", + script_name, '__main__.py') + p = spawn_python(zip_name) + p.stdin.write(b'l\n') + data = kill_python(p) + # bdb/pdb applies normcase to its filename before displaying + self.assertIn(os.path.normcase(run_name.encode('utf-8')), data) + + +def tearDownModule(): + test.support.reap_children() + +if __name__ == '__main__': + unittest.main() diff --git a/cpython-tests/cp38/zip_cp437_header.zip b/cpython-tests/cp38/zip_cp437_header.zip new file mode 100644 index 0000000000000000000000000000000000000000..f7c6cf170422c48ce1337a4c05843fe66a4b0bbf GIT binary patch literal 270 zcmWIWW@Zs#U}E54IGj1(Hjhth3p)b?11k`V0&!YqPHJ9aZfbmaW=Tf;#QZkBl8Tbz zujP-QGBE_;)G30lGrzP1MI$4V2m@|Qfx3Y}0Zig>LV!1JBS5x8fC7-R0%8=FkcTh5&DbW=z*2 uYc>UI2Du%m9b!Lz?W_zi?FI&&mdJ6*Ca-0n$rA90maKRyZ91 literal 0 HcmV?d00001 diff --git a/cpython-tests/cp38/ziptestdata/README.md b/cpython-tests/cp38/ziptestdata/README.md new file mode 100644 index 0000000..6b9147d --- /dev/null +++ b/cpython-tests/cp38/ziptestdata/README.md @@ -0,0 +1,35 @@ +# Test data for `test_zipfile` + +The test executables in this directory are created manually from header.sh and +the `testdata_module_inside_zip.py` file. You must have infozip's zip utility +installed (`apt install zip` on Debian). + +## Purpose + +These are used to test executable files with an appended zipfile, in a scenario +where the executable is _not_ a Python interpreter itself so our automatic +zipimport machinery (that'd look for `__main__.py`) is not being used. + +## Updating the test executables + +If you update header.sh or the testdata_module_inside_zip.py file, rerun the +commands below. These are expected to be rarely changed, if ever. + +### Standard old format (2.0) zip file + +``` +zip -0 zip2.zip testdata_module_inside_zip.py +cat header.sh zip2.zip >exe_with_zip +rm zip2.zip +``` + +### Modern format (4.5) zip64 file + +Redirecting from stdin forces infozip's zip tool to create a zip64. + +``` +zip -0 zip64.zip +cat header.sh zip64.zip >exe_with_z64 +rm zip64.zip +``` + diff --git a/cpython-tests/cp38/ziptestdata/exe_with_z64 b/cpython-tests/cp38/ziptestdata/exe_with_z64 new file mode 100644 index 0000000000000000000000000000000000000000..82b03cf39d919d9de05866b4f7cdf1dfe47e6eb3 GIT binary patch literal 978 zcmaJ<-EY${5KkEl$xl4ME4rnV(xNp330}q~w6RoFg|_N41e7XrQ)jhEoXByzLbV6} z7bN~nxXZ`b5aOcPclPh+`)uy)&!pO)@qEF01K%5u#vZQ0`QQ{+-#hbQJlMzfN zumhbn*t?s5Bd=_jPG5pq2*m(Jgo_mHo-#sbTHp%FGB+?21c5M360YVDOC^Boi)A8| zaqW`1mIj`)NHXt(_xjvFK6&e598YZ!YZ3l8f{q6rI6U+Qr@^orj6V8rh65&(EY$|m zyw<+SERwNcOz}kI84m>G zSHN@NP(AKCZFVWm;@bWsvo1d0s^NQ(q;qlPXs1m?Of5j_0ahSNH4rM0DoR1B`pzXg zmbq!Q2?j9dhGVD|)zyN}i{}esyQ-xKTZG$#>tt`JC1{4sF91y#s`x7`^Rh*e)YvZy zgkqqkaUCw?O1P{lg45-zR7)d3Et45`xQsPi8a|7~fpf#r#O@xyAC7yz7Yxqdop@t= z+GecTY{GH*C{6^9iZVG|$~dMm;TcwVF6O`^njW)|c@d2ZNMpBKJnC=V?N}s_K0g`u zf>&q1Drr~`txm&wV0p#0b-g#i7nomB!y-wOlGog%8hujhFq@*CrC0V>0$BJLY}9Yu zdAxPoGdZHaQ8}dT$9GygqyF~x9%(2wjr1B?@B4I!vMx6ZdG|^ES=ode_3v$y*}#wR GCH6PMMGy`E literal 0 HcmV?d00001 diff --git a/cpython-tests/cp38/ziptestdata/exe_with_zip b/cpython-tests/cp38/ziptestdata/exe_with_zip new file mode 100644 index 0000000000000000000000000000000000000000..c833cdf9f934b08fc449da077efae44aa9a06f18 GIT binary patch literal 990 zcmah{&2G~`5Ozt6#BxUB0BGt|a)MeHBzh^WP)i*V6_O}*5D+43W3QVP_S)KAHz`pi zcm>WJc^aOC$6(e@N~!o+%den-3;)Sr%Y!Z0+w(d{LAMq3-uf@P z9m3N*lNvI$JbmPO%o9e4pea*14H@ji{DR<>2{Yf&&6LZ;8JC$DI=^T*Ba%xlbR%}U zITKu*!h8w30IGn(BDw1{$&~BKrT>oSEll57hHpZeMQq=ZPSXIfv;d*Is6d=aFi`;) zaRyv0|GCCbxYCWL2?L0zrbu-GbtR)wnZ5)z7h1BgVd6I7ve+xfDrk(z4*+%OisT#$ zRkbMQ68mL{7!IasRE86N#$2)x!D-R6OmfXY6zLc{TyYHxO~(n_b*@}Av|9(SyZyHB z1)agGL$7a-nuOHrbvUS!;zZ!62(4hslf;Y(%~9cqML=USJ$k}b$;JhQk>6X~JFcw~ z%d9)^A9mZpvl9=`=Dly-vourMXb_;{MX9UeQ7N~ZpAY<7R_*(II{NZyIx1$jt(Dau zHOneZ9ejjVI+sG|%rH|rlgP`o7b`AXUNIxrip1vZklyjijR&>AvAb(Xm+RYSv;Bwb WTE+Dm&))IcO#@!RC&c}$ajc&zD=F;& literal 0 HcmV?d00001 diff --git a/cpython-tests/cp38/ziptestdata/header.sh b/cpython-tests/cp38/ziptestdata/header.sh new file mode 100644 index 0000000..52dc91a --- /dev/null +++ b/cpython-tests/cp38/ziptestdata/header.sh @@ -0,0 +1,24 @@ +#!/bin/bash +INTERPRETER_UNDER_TEST="$1" +if [[ ! -x "${INTERPRETER_UNDER_TEST}" ]]; then + echo "Interpreter must be the command line argument." + exit 4 +fi +EXECUTABLE="$0" exec "${INTERPRETER_UNDER_TEST}" -E - <e|f"g?h*i', ','), r'a\b,c_d_e_f_g_h_i') + self.assertEqual(san('../../foo../../ba..r', '/'), r'foo/ba..r') + + def test_extract_hackers_arcnames_common_cases(self): + common_hacknames = [ + ('../foo/bar', 'foo/bar'), + ('foo/../bar', 'foo/bar'), + ('foo/../../bar', 'foo/bar'), + ('foo/bar/..', 'foo/bar'), + ('./../foo/bar', 'foo/bar'), + ('/foo/bar', 'foo/bar'), + ('/foo/../bar', 'foo/bar'), + ('/foo/../../bar', 'foo/bar'), + ] + self._test_extract_hackers_arcnames(common_hacknames) + + @unittest.skipIf(os.path.sep != '\\', 'Requires \\ as path separator.') + def test_extract_hackers_arcnames_windows_only(self): + """Test combination of path fixing and windows name sanitization.""" + windows_hacknames = [ + (r'..\foo\bar', 'foo/bar'), + (r'..\/foo\/bar', 'foo/bar'), + (r'foo/\..\/bar', 'foo/bar'), + (r'foo\/../\bar', 'foo/bar'), + (r'C:foo/bar', 'foo/bar'), + (r'C:/foo/bar', 'foo/bar'), + (r'C://foo/bar', 'foo/bar'), + (r'C:\foo\bar', 'foo/bar'), + (r'//conky/mountpoint/foo/bar', 'foo/bar'), + (r'\\conky\mountpoint\foo\bar', 'foo/bar'), + (r'///conky/mountpoint/foo/bar', 'conky/mountpoint/foo/bar'), + (r'\\\conky\mountpoint\foo\bar', 'conky/mountpoint/foo/bar'), + (r'//conky//mountpoint/foo/bar', 'conky/mountpoint/foo/bar'), + (r'\\conky\\mountpoint\foo\bar', 'conky/mountpoint/foo/bar'), + (r'//?/C:/foo/bar', 'foo/bar'), + (r'\\?\C:\foo\bar', 'foo/bar'), + (r'C:/../C:/foo/bar', 'C_/foo/bar'), + (r'a:b\ce|f"g?h*i', 'b/c_d_e_f_g_h_i'), + ('../../foo../../ba..r', 'foo/ba..r'), + ] + self._test_extract_hackers_arcnames(windows_hacknames) + + @unittest.skipIf(os.path.sep != '/', r'Requires / as path separator.') + def test_extract_hackers_arcnames_posix_only(self): + posix_hacknames = [ + ('//foo/bar', 'foo/bar'), + ('../../foo../../ba..r', 'foo../ba..r'), + (r'foo/..\bar', r'foo/..\bar'), + ] + self._test_extract_hackers_arcnames(posix_hacknames) + + def _test_extract_hackers_arcnames(self, hacknames): + for arcname, fixedname in hacknames: + content = b'foobar' + arcname.encode() + with zipfile.ZipFile(TESTFN2, 'w', zipfile.ZIP_STORED) as zipfp: + zinfo = zipfile.ZipInfo() + # preserve backslashes + zinfo.filename = arcname + zinfo.external_attr = 0o600 << 16 + zipfp.writestr(zinfo, content) + + arcname = arcname.replace(os.sep, "/") + targetpath = os.path.join('target', 'subdir', 'subsub') + correctfile = os.path.join(targetpath, *fixedname.split('/')) + + with zipfile.ZipFile(TESTFN2, 'r') as zipfp: + writtenfile = zipfp.extract(arcname, targetpath) + self.assertEqual(writtenfile, correctfile, + msg='extract %r: %r != %r' % + (arcname, writtenfile, correctfile)) + self.check_file(correctfile, content) + rmtree('target') + + with zipfile.ZipFile(TESTFN2, 'r') as zipfp: + zipfp.extractall(targetpath) + self.check_file(correctfile, content) + rmtree('target') + + correctfile = os.path.join(os.getcwd(), *fixedname.split('/')) + + with zipfile.ZipFile(TESTFN2, 'r') as zipfp: + writtenfile = zipfp.extract(arcname) + self.assertEqual(writtenfile, correctfile, + msg="extract %r" % arcname) + self.check_file(correctfile, content) + rmtree(fixedname.split('/')[0]) + + with zipfile.ZipFile(TESTFN2, 'r') as zipfp: + zipfp.extractall() + self.check_file(correctfile, content) + rmtree(fixedname.split('/')[0]) + + unlink(TESTFN2) + + +class OtherTests(unittest.TestCase): + def test_open_via_zip_info(self): + # Create the ZIP archive + with zipfile.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED) as zipfp: + zipfp.writestr("name", "foo") + with self.assertWarns(UserWarning): + zipfp.writestr("name", "bar") + self.assertEqual(zipfp.namelist(), ["name"] * 2) + + with zipfile.ZipFile(TESTFN2, "r") as zipfp: + infos = zipfp.infolist() + data = b"" + for info in infos: + with zipfp.open(info) as zipopen: + data += zipopen.read() + self.assertIn(data, {b"foobar", b"barfoo"}) + data = b"" + for info in infos: + data += zipfp.read(info) + self.assertIn(data, {b"foobar", b"barfoo"}) + + def test_writestr_extended_local_header_issue1202(self): + with zipfile.ZipFile(TESTFN2, 'w') as orig_zip: + for data in 'abcdefghijklmnop': + zinfo = zipfile.ZipInfo(data) + zinfo.flag_bits |= 0x08 # Include an extended local header. + orig_zip.writestr(zinfo, data) + + def test_close(self): + """Check that the zipfile is closed after the 'with' block.""" + with zipfile.ZipFile(TESTFN2, "w") as zipfp: + for fpath, fdata in SMALL_TEST_DATA: + zipfp.writestr(fpath, fdata) + self.assertIsNotNone(zipfp.fp, 'zipfp is not open') + self.assertIsNone(zipfp.fp, 'zipfp is not closed') + + with zipfile.ZipFile(TESTFN2, "r") as zipfp: + self.assertIsNotNone(zipfp.fp, 'zipfp is not open') + self.assertIsNone(zipfp.fp, 'zipfp is not closed') + + def test_close_on_exception(self): + """Check that the zipfile is closed if an exception is raised in the + 'with' block.""" + with zipfile.ZipFile(TESTFN2, "w") as zipfp: + for fpath, fdata in SMALL_TEST_DATA: + zipfp.writestr(fpath, fdata) + + try: + with zipfile.ZipFile(TESTFN2, "r") as zipfp2: + raise zipfile.BadZipFile() + except zipfile.BadZipFile: + self.assertIsNone(zipfp2.fp, 'zipfp is not closed') + + def test_unsupported_version(self): + # File has an extract_version of 120 + data = (b'PK\x03\x04x\x00\x00\x00\x00\x00!p\xa1@\x00\x00\x00\x00\x00\x00' + b'\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00xPK\x01\x02x\x03x\x00\x00\x00\x00' + b'\x00!p\xa1@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00' + b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x01\x00\x00\x00\x00xPK\x05\x06' + b'\x00\x00\x00\x00\x01\x00\x01\x00/\x00\x00\x00\x1f\x00\x00\x00\x00\x00') + + self.assertRaises(NotImplementedError, zipfile.ZipFile, + io.BytesIO(data), 'r') + + @requires_zlib() + def test_read_unicode_filenames(self): + # bug #10801 + fname = findfile('zip_cp437_header.zip') + with zipfile.ZipFile(fname) as zipfp: + for name in zipfp.namelist(): + zipfp.open(name).close() + + def test_write_unicode_filenames(self): + with zipfile.ZipFile(TESTFN, "w") as zf: + zf.writestr("foo.txt", "Test for unicode filename") + zf.writestr("\xf6.txt", "Test for unicode filename") + self.assertIsInstance(zf.infolist()[0].filename, str) + + with zipfile.ZipFile(TESTFN, "r") as zf: + self.assertEqual(zf.filelist[0].filename, "foo.txt") + self.assertEqual(zf.filelist[1].filename, "\xf6.txt") + + def test_read_after_write_unicode_filenames(self): + with zipfile.ZipFile(TESTFN2, 'w') as zipfp: + zipfp.writestr('приклад', b'sample') + self.assertEqual(zipfp.read('приклад'), b'sample') + + def test_exclusive_create_zip_file(self): + """Test exclusive creating a new zipfile.""" + unlink(TESTFN2) + filename = 'testfile.txt' + content = b'hello, world. this is some content.' + with zipfile.ZipFile(TESTFN2, "x", zipfile.ZIP_STORED) as zipfp: + zipfp.writestr(filename, content) + with self.assertRaises(FileExistsError): + zipfile.ZipFile(TESTFN2, "x", zipfile.ZIP_STORED) + with zipfile.ZipFile(TESTFN2, "r") as zipfp: + self.assertEqual(zipfp.namelist(), [filename]) + self.assertEqual(zipfp.read(filename), content) + + def test_create_non_existent_file_for_append(self): + if os.path.exists(TESTFN): + os.unlink(TESTFN) + + filename = 'testfile.txt' + content = b'hello, world. this is some content.' + + try: + with zipfile.ZipFile(TESTFN, 'a') as zf: + zf.writestr(filename, content) + except OSError: + self.fail('Could not append data to a non-existent zip file.') + + self.assertTrue(os.path.exists(TESTFN)) + + with zipfile.ZipFile(TESTFN, 'r') as zf: + self.assertEqual(zf.read(filename), content) + + def test_close_erroneous_file(self): + # This test checks that the ZipFile constructor closes the file object + # it opens if there's an error in the file. If it doesn't, the + # traceback holds a reference to the ZipFile object and, indirectly, + # the file object. + # On Windows, this causes the os.unlink() call to fail because the + # underlying file is still open. This is SF bug #412214. + # + with open(TESTFN, "w") as fp: + fp.write("this is not a legal zip file\n") + try: + zf = zipfile.ZipFile(TESTFN) + except zipfile.BadZipFile: + pass + + def test_is_zip_erroneous_file(self): + """Check that is_zipfile() correctly identifies non-zip files.""" + # - passing a filename + with open(TESTFN, "w") as fp: + fp.write("this is not a legal zip file\n") + self.assertFalse(zipfile.is_zipfile(TESTFN)) + # - passing a path-like object + self.assertFalse(zipfile.is_zipfile(pathlib.Path(TESTFN))) + # - passing a file object + with open(TESTFN, "rb") as fp: + self.assertFalse(zipfile.is_zipfile(fp)) + # - passing a file-like object + fp = io.BytesIO() + fp.write(b"this is not a legal zip file\n") + self.assertFalse(zipfile.is_zipfile(fp)) + fp.seek(0, 0) + self.assertFalse(zipfile.is_zipfile(fp)) + + def test_damaged_zipfile(self): + """Check that zipfiles with missing bytes at the end raise BadZipFile.""" + # - Create a valid zip file + fp = io.BytesIO() + with zipfile.ZipFile(fp, mode="w") as zipf: + zipf.writestr("foo.txt", b"O, for a Muse of Fire!") + zipfiledata = fp.getvalue() + + # - Now create copies of it missing the last N bytes and make sure + # a BadZipFile exception is raised when we try to open it + for N in range(len(zipfiledata)): + fp = io.BytesIO(zipfiledata[:N]) + self.assertRaises(zipfile.BadZipFile, zipfile.ZipFile, fp) + + def test_is_zip_valid_file(self): + """Check that is_zipfile() correctly identifies zip files.""" + # - passing a filename + with zipfile.ZipFile(TESTFN, mode="w") as zipf: + zipf.writestr("foo.txt", b"O, for a Muse of Fire!") + + self.assertTrue(zipfile.is_zipfile(TESTFN)) + # - passing a file object + with open(TESTFN, "rb") as fp: + self.assertTrue(zipfile.is_zipfile(fp)) + fp.seek(0, 0) + zip_contents = fp.read() + # - passing a file-like object + fp = io.BytesIO() + fp.write(zip_contents) + self.assertTrue(zipfile.is_zipfile(fp)) + fp.seek(0, 0) + self.assertTrue(zipfile.is_zipfile(fp)) + + def test_non_existent_file_raises_OSError(self): + # make sure we don't raise an AttributeError when a partially-constructed + # ZipFile instance is finalized; this tests for regression on SF tracker + # bug #403871. + + # The bug we're testing for caused an AttributeError to be raised + # when a ZipFile instance was created for a file that did not + # exist; the .fp member was not initialized but was needed by the + # __del__() method. Since the AttributeError is in the __del__(), + # it is ignored, but the user should be sufficiently annoyed by + # the message on the output that regression will be noticed + # quickly. + self.assertRaises(OSError, zipfile.ZipFile, TESTFN) + + def test_empty_file_raises_BadZipFile(self): + f = open(TESTFN, 'w') + f.close() + self.assertRaises(zipfile.BadZipFile, zipfile.ZipFile, TESTFN) + + with open(TESTFN, 'w') as fp: + fp.write("short file") + self.assertRaises(zipfile.BadZipFile, zipfile.ZipFile, TESTFN) + + def test_closed_zip_raises_ValueError(self): + """Verify that testzip() doesn't swallow inappropriate exceptions.""" + data = io.BytesIO() + with zipfile.ZipFile(data, mode="w") as zipf: + zipf.writestr("foo.txt", "O, for a Muse of Fire!") + + # This is correct; calling .read on a closed ZipFile should raise + # a ValueError, and so should calling .testzip. An earlier + # version of .testzip would swallow this exception (and any other) + # and report that the first file in the archive was corrupt. + self.assertRaises(ValueError, zipf.read, "foo.txt") + self.assertRaises(ValueError, zipf.open, "foo.txt") + self.assertRaises(ValueError, zipf.testzip) + self.assertRaises(ValueError, zipf.writestr, "bogus.txt", "bogus") + with open(TESTFN, 'w') as f: + f.write('zipfile test data') + self.assertRaises(ValueError, zipf.write, TESTFN) + + def test_bad_constructor_mode(self): + """Check that bad modes passed to ZipFile constructor are caught.""" + self.assertRaises(ValueError, zipfile.ZipFile, TESTFN, "q") + + def test_bad_open_mode(self): + """Check that bad modes passed to ZipFile.open are caught.""" + with zipfile.ZipFile(TESTFN, mode="w") as zipf: + zipf.writestr("foo.txt", "O, for a Muse of Fire!") + + with zipfile.ZipFile(TESTFN, mode="r") as zipf: + # read the data to make sure the file is there + zipf.read("foo.txt") + self.assertRaises(ValueError, zipf.open, "foo.txt", "q") + # universal newlines support is removed + self.assertRaises(ValueError, zipf.open, "foo.txt", "U") + self.assertRaises(ValueError, zipf.open, "foo.txt", "rU") + + def test_read0(self): + """Check that calling read(0) on a ZipExtFile object returns an empty + string and doesn't advance file pointer.""" + with zipfile.ZipFile(TESTFN, mode="w") as zipf: + zipf.writestr("foo.txt", "O, for a Muse of Fire!") + # read the data to make sure the file is there + with zipf.open("foo.txt") as f: + for i in range(FIXEDTEST_SIZE): + self.assertEqual(f.read(0), b'') + + self.assertEqual(f.read(), b"O, for a Muse of Fire!") + + def test_open_non_existent_item(self): + """Check that attempting to call open() for an item that doesn't + exist in the archive raises a RuntimeError.""" + with zipfile.ZipFile(TESTFN, mode="w") as zipf: + self.assertRaises(KeyError, zipf.open, "foo.txt", "r") + + def test_bad_compression_mode(self): + """Check that bad compression methods passed to ZipFile.open are + caught.""" + self.assertRaises(NotImplementedError, zipfile.ZipFile, TESTFN, "w", -1) + + def test_unsupported_compression(self): + # data is declared as shrunk, but actually deflated + data = (b'PK\x03\x04.\x00\x00\x00\x01\x00\xe4C\xa1@\x00\x00\x00' + b'\x00\x02\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00x\x03\x00PK\x01' + b'\x02.\x03.\x00\x00\x00\x01\x00\xe4C\xa1@\x00\x00\x00\x00\x02\x00\x00' + b'\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + b'\x80\x01\x00\x00\x00\x00xPK\x05\x06\x00\x00\x00\x00\x01\x00\x01\x00' + b'/\x00\x00\x00!\x00\x00\x00\x00\x00') + with zipfile.ZipFile(io.BytesIO(data), 'r') as zipf: + self.assertRaises(NotImplementedError, zipf.open, 'x') + + def test_null_byte_in_filename(self): + """Check that a filename containing a null byte is properly + terminated.""" + with zipfile.ZipFile(TESTFN, mode="w") as zipf: + zipf.writestr("foo.txt\x00qqq", b"O, for a Muse of Fire!") + self.assertEqual(zipf.namelist(), ['foo.txt']) + + def test_struct_sizes(self): + """Check that ZIP internal structure sizes are calculated correctly.""" + self.assertEqual(zipfile.sizeEndCentDir, 22) + self.assertEqual(zipfile.sizeCentralDir, 46) + self.assertEqual(zipfile.sizeEndCentDir64, 56) + self.assertEqual(zipfile.sizeEndCentDir64Locator, 20) + + def test_comments(self): + """Check that comments on the archive are handled properly.""" + + # check default comment is empty + with zipfile.ZipFile(TESTFN, mode="w") as zipf: + self.assertEqual(zipf.comment, b'') + zipf.writestr("foo.txt", "O, for a Muse of Fire!") + + with zipfile.ZipFile(TESTFN, mode="r") as zipfr: + self.assertEqual(zipfr.comment, b'') + + # check a simple short comment + comment = b'Bravely taking to his feet, he beat a very brave retreat.' + with zipfile.ZipFile(TESTFN, mode="w") as zipf: + zipf.comment = comment + zipf.writestr("foo.txt", "O, for a Muse of Fire!") + with zipfile.ZipFile(TESTFN, mode="r") as zipfr: + self.assertEqual(zipf.comment, comment) + + # check a comment of max length + comment2 = ''.join(['%d' % (i**3 % 10) for i in range((1 << 16)-1)]) + comment2 = comment2.encode("ascii") + with zipfile.ZipFile(TESTFN, mode="w") as zipf: + zipf.comment = comment2 + zipf.writestr("foo.txt", "O, for a Muse of Fire!") + + with zipfile.ZipFile(TESTFN, mode="r") as zipfr: + self.assertEqual(zipfr.comment, comment2) + + # check a comment that is too long is truncated + with zipfile.ZipFile(TESTFN, mode="w") as zipf: + with self.assertWarns(UserWarning): + zipf.comment = comment2 + b'oops' + zipf.writestr("foo.txt", "O, for a Muse of Fire!") + with zipfile.ZipFile(TESTFN, mode="r") as zipfr: + self.assertEqual(zipfr.comment, comment2) + + # check that comments are correctly modified in append mode + with zipfile.ZipFile(TESTFN,mode="w") as zipf: + zipf.comment = b"original comment" + zipf.writestr("foo.txt", "O, for a Muse of Fire!") + with zipfile.ZipFile(TESTFN,mode="a") as zipf: + zipf.comment = b"an updated comment" + with zipfile.ZipFile(TESTFN,mode="r") as zipf: + self.assertEqual(zipf.comment, b"an updated comment") + + # check that comments are correctly shortened in append mode + # and the file is indeed truncated + with zipfile.ZipFile(TESTFN,mode="w") as zipf: + zipf.comment = b"original comment that's longer" + zipf.writestr("foo.txt", "O, for a Muse of Fire!") + original_zip_size = os.path.getsize(TESTFN) + with zipfile.ZipFile(TESTFN,mode="a") as zipf: + zipf.comment = b"shorter comment" + self.assertTrue(original_zip_size > os.path.getsize(TESTFN)) + with zipfile.ZipFile(TESTFN,mode="r") as zipf: + self.assertEqual(zipf.comment, b"shorter comment") + + def test_unicode_comment(self): + with zipfile.ZipFile(TESTFN, "w", zipfile.ZIP_STORED) as zipf: + zipf.writestr("foo.txt", "O, for a Muse of Fire!") + with self.assertRaises(TypeError): + zipf.comment = "this is an error" + + def test_change_comment_in_empty_archive(self): + with zipfile.ZipFile(TESTFN, "a", zipfile.ZIP_STORED) as zipf: + self.assertFalse(zipf.filelist) + zipf.comment = b"this is a comment" + with zipfile.ZipFile(TESTFN, "r") as zipf: + self.assertEqual(zipf.comment, b"this is a comment") + + def test_change_comment_in_nonempty_archive(self): + with zipfile.ZipFile(TESTFN, "w", zipfile.ZIP_STORED) as zipf: + zipf.writestr("foo.txt", "O, for a Muse of Fire!") + with zipfile.ZipFile(TESTFN, "a", zipfile.ZIP_STORED) as zipf: + self.assertTrue(zipf.filelist) + zipf.comment = b"this is a comment" + with zipfile.ZipFile(TESTFN, "r") as zipf: + self.assertEqual(zipf.comment, b"this is a comment") + + def test_empty_zipfile(self): + # Check that creating a file in 'w' or 'a' mode and closing without + # adding any files to the archives creates a valid empty ZIP file + zipf = zipfile.ZipFile(TESTFN, mode="w") + zipf.close() + try: + zipf = zipfile.ZipFile(TESTFN, mode="r") + except zipfile.BadZipFile: + self.fail("Unable to create empty ZIP file in 'w' mode") + + zipf = zipfile.ZipFile(TESTFN, mode="a") + zipf.close() + try: + zipf = zipfile.ZipFile(TESTFN, mode="r") + except: + self.fail("Unable to create empty ZIP file in 'a' mode") + + def test_open_empty_file(self): + # Issue 1710703: Check that opening a file with less than 22 bytes + # raises a BadZipFile exception (rather than the previously unhelpful + # OSError) + f = open(TESTFN, 'w') + f.close() + self.assertRaises(zipfile.BadZipFile, zipfile.ZipFile, TESTFN, 'r') + + def test_create_zipinfo_before_1980(self): + self.assertRaises(ValueError, + zipfile.ZipInfo, 'seventies', (1979, 1, 1, 0, 0, 0)) + + def test_create_empty_zipinfo_repr(self): + """Before bpo-26185, repr() on empty ZipInfo object was failing.""" + zi = zipfile.ZipInfo(filename="empty") + self.assertEqual(repr(zi), "") + + def test_create_empty_zipinfo_default_attributes(self): + """Ensure all required attributes are set.""" + zi = zipfile.ZipInfo() + self.assertEqual(zi.orig_filename, "NoName") + self.assertEqual(zi.filename, "NoName") + self.assertEqual(zi.date_time, (1980, 1, 1, 0, 0, 0)) + self.assertEqual(zi.compress_type, zipfile.ZIP_STORED) + self.assertEqual(zi.comment, b"") + self.assertEqual(zi.extra, b"") + self.assertIn(zi.create_system, (0, 3)) + self.assertEqual(zi.create_version, zipfile.DEFAULT_VERSION) + self.assertEqual(zi.extract_version, zipfile.DEFAULT_VERSION) + self.assertEqual(zi.reserved, 0) + self.assertEqual(zi.flag_bits, 0) + self.assertEqual(zi.volume, 0) + self.assertEqual(zi.internal_attr, 0) + self.assertEqual(zi.external_attr, 0) + + # Before bpo-26185, both were missing + self.assertEqual(zi.file_size, 0) + self.assertEqual(zi.compress_size, 0) + + def test_zipfile_with_short_extra_field(self): + """If an extra field in the header is less than 4 bytes, skip it.""" + zipdata = ( + b'PK\x03\x04\x14\x00\x00\x00\x00\x00\x93\x9b\xad@\x8b\x9e' + b'\xd9\xd3\x01\x00\x00\x00\x01\x00\x00\x00\x03\x00\x03\x00ab' + b'c\x00\x00\x00APK\x01\x02\x14\x03\x14\x00\x00\x00\x00' + b'\x00\x93\x9b\xad@\x8b\x9e\xd9\xd3\x01\x00\x00\x00\x01\x00\x00' + b'\x00\x03\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x00' + b'\x00\x00\x00abc\x00\x00PK\x05\x06\x00\x00\x00\x00' + b'\x01\x00\x01\x003\x00\x00\x00%\x00\x00\x00\x00\x00' + ) + with zipfile.ZipFile(io.BytesIO(zipdata), 'r') as zipf: + # testzip returns the name of the first corrupt file, or None + self.assertIsNone(zipf.testzip()) + + def test_open_conflicting_handles(self): + # It's only possible to open one writable file handle at a time + msg1 = b"It's fun to charter an accountant!" + msg2 = b"And sail the wide accountant sea" + msg3 = b"To find, explore the funds offshore" + with zipfile.ZipFile(TESTFN2, 'w', zipfile.ZIP_STORED) as zipf: + with zipf.open('foo', mode='w') as w2: + w2.write(msg1) + with zipf.open('bar', mode='w') as w1: + with self.assertRaises(ValueError): + zipf.open('handle', mode='w') + with self.assertRaises(ValueError): + zipf.open('foo', mode='r') + with self.assertRaises(ValueError): + zipf.writestr('str', 'abcde') + with self.assertRaises(ValueError): + zipf.write(__file__, 'file') + with self.assertRaises(ValueError): + zipf.close() + w1.write(msg2) + with zipf.open('baz', mode='w') as w2: + w2.write(msg3) + + with zipfile.ZipFile(TESTFN2, 'r') as zipf: + self.assertEqual(zipf.read('foo'), msg1) + self.assertEqual(zipf.read('bar'), msg2) + self.assertEqual(zipf.read('baz'), msg3) + self.assertEqual(zipf.namelist(), ['foo', 'bar', 'baz']) + + def test_seek_tell(self): + # Test seek functionality + txt = b"Where's Bruce?" + bloc = txt.find(b"Bruce") + # Check seek on a file + with zipfile.ZipFile(TESTFN, "w") as zipf: + zipf.writestr("foo.txt", txt) + with zipfile.ZipFile(TESTFN, "r") as zipf: + with zipf.open("foo.txt", "r") as fp: + fp.seek(bloc, os.SEEK_SET) + self.assertEqual(fp.tell(), bloc) + fp.seek(-bloc, os.SEEK_CUR) + self.assertEqual(fp.tell(), 0) + fp.seek(bloc, os.SEEK_CUR) + self.assertEqual(fp.tell(), bloc) + self.assertEqual(fp.read(5), txt[bloc:bloc+5]) + fp.seek(0, os.SEEK_END) + self.assertEqual(fp.tell(), len(txt)) + fp.seek(0, os.SEEK_SET) + self.assertEqual(fp.tell(), 0) + # Check seek on memory file + data = io.BytesIO() + with zipfile.ZipFile(data, mode="w") as zipf: + zipf.writestr("foo.txt", txt) + with zipfile.ZipFile(data, mode="r") as zipf: + with zipf.open("foo.txt", "r") as fp: + fp.seek(bloc, os.SEEK_SET) + self.assertEqual(fp.tell(), bloc) + fp.seek(-bloc, os.SEEK_CUR) + self.assertEqual(fp.tell(), 0) + fp.seek(bloc, os.SEEK_CUR) + self.assertEqual(fp.tell(), bloc) + self.assertEqual(fp.read(5), txt[bloc:bloc+5]) + fp.seek(0, os.SEEK_END) + self.assertEqual(fp.tell(), len(txt)) + fp.seek(0, os.SEEK_SET) + self.assertEqual(fp.tell(), 0) + + @requires_bz2() + def test_decompress_without_3rd_party_library(self): + data = b'PK\x05\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + zip_file = io.BytesIO(data) + with zipfile.ZipFile(zip_file, 'w', compression=zipfile.ZIP_BZIP2) as zf: + zf.writestr('a.txt', b'a') + with mock.patch('zipfile.bz2', None): + with zipfile.ZipFile(zip_file) as zf: + self.assertRaises(RuntimeError, zf.extract, 'a.txt') + + @requires_zlib() + def test_full_overlap(self): + data = ( + b'PK\x03\x04\x14\x00\x00\x00\x08\x00\xa0lH\x05\xe2\x1e' + b'8\xbb\x10\x00\x00\x00\t\x04\x00\x00\x01\x00\x00\x00a\xed' + b'\xc0\x81\x08\x00\x00\x00\xc00\xd6\xfbK\\d\x0b`P' + b'K\x01\x02\x14\x00\x14\x00\x00\x00\x08\x00\xa0lH\x05\xe2' + b'\x1e8\xbb\x10\x00\x00\x00\t\x04\x00\x00\x01\x00\x00\x00\x00' + b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00aPK' + b'\x01\x02\x14\x00\x14\x00\x00\x00\x08\x00\xa0lH\x05\xe2\x1e' + b'8\xbb\x10\x00\x00\x00\t\x04\x00\x00\x01\x00\x00\x00\x00\x00' + b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00bPK\x05' + b'\x06\x00\x00\x00\x00\x02\x00\x02\x00^\x00\x00\x00/\x00\x00' + b'\x00\x00\x00' + ) + with zipfile.ZipFile(io.BytesIO(data), 'r') as zipf: + self.assertEqual(zipf.namelist(), ['a', 'b']) + zi = zipf.getinfo('a') + self.assertEqual(zi.header_offset, 0) + self.assertEqual(zi.compress_size, 16) + self.assertEqual(zi.file_size, 1033) + zi = zipf.getinfo('b') + self.assertEqual(zi.header_offset, 0) + self.assertEqual(zi.compress_size, 16) + self.assertEqual(zi.file_size, 1033) + self.assertEqual(len(zipf.read('a')), 1033) + with self.assertRaisesRegex(zipfile.BadZipFile, 'File name.*differ'): + zipf.read('b') + + @requires_zlib() + def test_quoted_overlap(self): + data = ( + b'PK\x03\x04\x14\x00\x00\x00\x08\x00\xa0lH\x05Y\xfc' + b'8\x044\x00\x00\x00(\x04\x00\x00\x01\x00\x00\x00a\x00' + b'\x1f\x00\xe0\xffPK\x03\x04\x14\x00\x00\x00\x08\x00\xa0l' + b'H\x05\xe2\x1e8\xbb\x10\x00\x00\x00\t\x04\x00\x00\x01\x00' + b'\x00\x00b\xed\xc0\x81\x08\x00\x00\x00\xc00\xd6\xfbK\\' + b'd\x0b`PK\x01\x02\x14\x00\x14\x00\x00\x00\x08\x00\xa0' + b'lH\x05Y\xfc8\x044\x00\x00\x00(\x04\x00\x00\x01' + b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + b'\x00aPK\x01\x02\x14\x00\x14\x00\x00\x00\x08\x00\xa0l' + b'H\x05\xe2\x1e8\xbb\x10\x00\x00\x00\t\x04\x00\x00\x01\x00' + b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00$\x00\x00\x00' + b'bPK\x05\x06\x00\x00\x00\x00\x02\x00\x02\x00^\x00\x00' + b'\x00S\x00\x00\x00\x00\x00' + ) + with zipfile.ZipFile(io.BytesIO(data), 'r') as zipf: + self.assertEqual(zipf.namelist(), ['a', 'b']) + zi = zipf.getinfo('a') + self.assertEqual(zi.header_offset, 0) + self.assertEqual(zi.compress_size, 52) + self.assertEqual(zi.file_size, 1064) + zi = zipf.getinfo('b') + self.assertEqual(zi.header_offset, 36) + self.assertEqual(zi.compress_size, 16) + self.assertEqual(zi.file_size, 1033) + with self.assertRaisesRegex(zipfile.BadZipFile, 'Overlapped entries'): + zipf.read('a') + self.assertEqual(len(zipf.read('b')), 1033) + + def tearDown(self): + unlink(TESTFN) + unlink(TESTFN2) + + +class AbstractBadCrcTests: + def test_testzip_with_bad_crc(self): + """Tests that files with bad CRCs return their name from testzip.""" + zipdata = self.zip_with_bad_crc + + with zipfile.ZipFile(io.BytesIO(zipdata), mode="r") as zipf: + # testzip returns the name of the first corrupt file, or None + self.assertEqual('afile', zipf.testzip()) + + def test_read_with_bad_crc(self): + """Tests that files with bad CRCs raise a BadZipFile exception when read.""" + zipdata = self.zip_with_bad_crc + + # Using ZipFile.read() + with zipfile.ZipFile(io.BytesIO(zipdata), mode="r") as zipf: + self.assertRaises(zipfile.BadZipFile, zipf.read, 'afile') + + # Using ZipExtFile.read() + with zipfile.ZipFile(io.BytesIO(zipdata), mode="r") as zipf: + with zipf.open('afile', 'r') as corrupt_file: + self.assertRaises(zipfile.BadZipFile, corrupt_file.read) + + # Same with small reads (in order to exercise the buffering logic) + with zipfile.ZipFile(io.BytesIO(zipdata), mode="r") as zipf: + with zipf.open('afile', 'r') as corrupt_file: + corrupt_file.MIN_READ_SIZE = 2 + with self.assertRaises(zipfile.BadZipFile): + while corrupt_file.read(2): + pass + + +class StoredBadCrcTests(AbstractBadCrcTests, unittest.TestCase): + compression = zipfile.ZIP_STORED + zip_with_bad_crc = ( + b'PK\003\004\024\0\0\0\0\0 \213\212;:r' + b'\253\377\f\0\0\0\f\0\0\0\005\0\0\000af' + b'ilehello,AworldP' + b'K\001\002\024\003\024\0\0\0\0\0 \213\212;:' + b'r\253\377\f\0\0\0\f\0\0\0\005\0\0\0\0' + b'\0\0\0\0\0\0\0\200\001\0\0\0\000afi' + b'lePK\005\006\0\0\0\0\001\0\001\0003\000' + b'\0\0/\0\0\0\0\0') + +@requires_zlib() +class DeflateBadCrcTests(AbstractBadCrcTests, unittest.TestCase): + compression = zipfile.ZIP_DEFLATED + zip_with_bad_crc = ( + b'PK\x03\x04\x14\x00\x00\x00\x08\x00n}\x0c=FA' + b'KE\x10\x00\x00\x00n\x00\x00\x00\x05\x00\x00\x00af' + b'ile\xcbH\xcd\xc9\xc9W(\xcf/\xcaI\xc9\xa0' + b'=\x13\x00PK\x01\x02\x14\x03\x14\x00\x00\x00\x08\x00n' + b'}\x0c=FAKE\x10\x00\x00\x00n\x00\x00\x00\x05' + b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x01\x00\x00\x00' + b'\x00afilePK\x05\x06\x00\x00\x00\x00\x01\x00' + b'\x01\x003\x00\x00\x003\x00\x00\x00\x00\x00') + +@requires_bz2() +class Bzip2BadCrcTests(AbstractBadCrcTests, unittest.TestCase): + compression = zipfile.ZIP_BZIP2 + zip_with_bad_crc = ( + b'PK\x03\x04\x14\x03\x00\x00\x0c\x00nu\x0c=FA' + b'KE8\x00\x00\x00n\x00\x00\x00\x05\x00\x00\x00af' + b'ileBZh91AY&SY\xd4\xa8\xca' + b'\x7f\x00\x00\x0f\x11\x80@\x00\x06D\x90\x80 \x00 \xa5' + b'P\xd9!\x03\x03\x13\x13\x13\x89\xa9\xa9\xc2u5:\x9f' + b'\x8b\xb9"\x9c(HjTe?\x80PK\x01\x02\x14' + b'\x03\x14\x03\x00\x00\x0c\x00nu\x0c=FAKE8' + b'\x00\x00\x00n\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x00' + b'\x00 \x80\x80\x81\x00\x00\x00\x00afilePK' + b'\x05\x06\x00\x00\x00\x00\x01\x00\x01\x003\x00\x00\x00[\x00' + b'\x00\x00\x00\x00') + +@requires_lzma() +class LzmaBadCrcTests(AbstractBadCrcTests, unittest.TestCase): + compression = zipfile.ZIP_LZMA + zip_with_bad_crc = ( + b'PK\x03\x04\x14\x03\x00\x00\x0e\x00nu\x0c=FA' + b'KE\x1b\x00\x00\x00n\x00\x00\x00\x05\x00\x00\x00af' + b'ile\t\x04\x05\x00]\x00\x00\x00\x04\x004\x19I' + b'\xee\x8d\xe9\x17\x89:3`\tq!.8\x00PK' + b'\x01\x02\x14\x03\x14\x03\x00\x00\x0e\x00nu\x0c=FA' + b'KE\x1b\x00\x00\x00n\x00\x00\x00\x05\x00\x00\x00\x00\x00' + b'\x00\x00\x00\x00 \x80\x80\x81\x00\x00\x00\x00afil' + b'ePK\x05\x06\x00\x00\x00\x00\x01\x00\x01\x003\x00\x00' + b'\x00>\x00\x00\x00\x00\x00') + + +class DecryptionTests(unittest.TestCase): + """Check that ZIP decryption works. Since the library does not + support encryption at the moment, we use a pre-generated encrypted + ZIP file.""" + + data = ( + b'PK\x03\x04\x14\x00\x01\x00\x00\x00n\x92i.#y\xef?&\x00\x00\x00\x1a\x00' + b'\x00\x00\x08\x00\x00\x00test.txt\xfa\x10\xa0gly|\xfa-\xc5\xc0=\xf9y' + b'\x18\xe0\xa8r\xb3Z}Lg\xbc\xae\xf9|\x9b\x19\xe4\x8b\xba\xbb)\x8c\xb0\xdbl' + b'PK\x01\x02\x14\x00\x14\x00\x01\x00\x00\x00n\x92i.#y\xef?&\x00\x00\x00' + b'\x1a\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00\x00\x01\x00 \x00\xb6\x81' + b'\x00\x00\x00\x00test.txtPK\x05\x06\x00\x00\x00\x00\x01\x00\x01\x006\x00' + b'\x00\x00L\x00\x00\x00\x00\x00' ) + data2 = ( + b'PK\x03\x04\x14\x00\t\x00\x08\x00\xcf}38xu\xaa\xb2\x14\x00\x00\x00\x00\x02' + b'\x00\x00\x04\x00\x15\x00zeroUT\t\x00\x03\xd6\x8b\x92G\xda\x8b\x92GUx\x04' + b'\x00\xe8\x03\xe8\x03\xc7", "exec") +raise_src = 'def do_raise(): raise TypeError\n' + +def make_pyc(co, mtime, size): + data = marshal.dumps(co) + pyc = (importlib.util.MAGIC_NUMBER + + struct.pack("", "exec"), NOW, len(src)) + files = {TESTMOD + pyc_ext: (NOW, pyc), + "some.data": (NOW, "some data")} + self.doTest(pyc_ext, files, TESTMOD) + + def testDefaultOptimizationLevel(self): + # zipimport should use the default optimization level (#28131) + src = """if 1: # indent hack + def test(val): + assert(val) + return val\n""" + files = {TESTMOD + '.py': (NOW, src)} + self.makeZip(files) + sys.path.insert(0, TEMP_ZIP) + mod = importlib.import_module(TESTMOD) + self.assertEqual(mod.test(1), 1) + self.assertRaises(AssertionError, mod.test, False) + + def testImport_WithStuff(self): + # try importing from a zipfile which contains additional + # stuff at the beginning of the file + files = {TESTMOD + ".py": (NOW, test_src)} + self.doTest(".py", files, TESTMOD, + stuff=b"Some Stuff"*31) + + def assertModuleSource(self, module): + self.assertEqual(inspect.getsource(module), test_src) + + def testGetSource(self): + files = {TESTMOD + ".py": (NOW, test_src)} + self.doTest(".py", files, TESTMOD, call=self.assertModuleSource) + + def testGetCompiledSource(self): + pyc = make_pyc(compile(test_src, "", "exec"), NOW, len(test_src)) + files = {TESTMOD + ".py": (NOW, test_src), + TESTMOD + pyc_ext: (NOW, pyc)} + self.doTest(pyc_ext, files, TESTMOD, call=self.assertModuleSource) + + def runDoctest(self, callback): + files = {TESTMOD + ".py": (NOW, test_src), + "xyz.txt": (NOW, ">>> log.append(True)\n")} + self.doTest(".py", files, TESTMOD, call=callback) + + def doDoctestFile(self, module): + log = [] + old_master, doctest.master = doctest.master, None + try: + doctest.testfile( + 'xyz.txt', package=module, module_relative=True, + globs=locals() + ) + finally: + doctest.master = old_master + self.assertEqual(log,[True]) + + def testDoctestFile(self): + self.runDoctest(self.doDoctestFile) + + def doDoctestSuite(self, module): + log = [] + doctest.DocFileTest( + 'xyz.txt', package=module, module_relative=True, + globs=locals() + ).run() + self.assertEqual(log,[True]) + + def testDoctestSuite(self): + self.runDoctest(self.doDoctestSuite) + + def doTraceback(self, module): + try: + module.do_raise() + except Exception as e: + tb = e.__traceback__.tb_next + + f,lno,n,line = extract_tb(tb, 1)[0] + self.assertEqual(line, raise_src.strip()) + + f,lno,n,line = extract_stack(tb.tb_frame, 1)[0] + self.assertEqual(line, raise_src.strip()) + + s = io.StringIO() + print_tb(tb, 1, s) + self.assertTrue(s.getvalue().endswith(raise_src)) + else: + raise AssertionError("This ought to be impossible") + + def testTraceback(self): + files = {TESTMOD + ".py": (NOW, raise_src)} + self.doTest(None, files, TESTMOD, call=self.doTraceback) + + @unittest.skipIf(support.TESTFN_UNENCODABLE is None, + "need an unencodable filename") + def testUnencodable(self): + filename = support.TESTFN_UNENCODABLE + ".zip" + self.addCleanup(support.unlink, filename) + with ZipFile(filename, "w") as z: + zinfo = ZipInfo(TESTMOD + ".py", time.localtime(NOW)) + zinfo.compress_type = self.compression + z.writestr(zinfo, test_src) + zipimport.zipimporter(filename).load_module(TESTMOD) + + def testBytesPath(self): + filename = support.TESTFN + ".zip" + self.addCleanup(support.unlink, filename) + with ZipFile(filename, "w") as z: + zinfo = ZipInfo(TESTMOD + ".py", time.localtime(NOW)) + zinfo.compress_type = self.compression + z.writestr(zinfo, test_src) + + zipimport.zipimporter(filename) + zipimport.zipimporter(os.fsencode(filename)) + with self.assertRaises(TypeError): + zipimport.zipimporter(bytearray(os.fsencode(filename))) + with self.assertRaises(TypeError): + zipimport.zipimporter(memoryview(os.fsencode(filename))) + + def testComment(self): + files = {TESTMOD + ".py": (NOW, test_src)} + self.doTest(".py", files, TESTMOD, comment=b"comment") + + def testBeginningCruftAndComment(self): + files = {TESTMOD + ".py": (NOW, test_src)} + self.doTest(".py", files, TESTMOD, stuff=b"cruft" * 64, comment=b"hi") + + def testLargestPossibleComment(self): + files = {TESTMOD + ".py": (NOW, test_src)} + self.doTest(".py", files, TESTMOD, comment=b"c" * ((1 << 16) - 1)) + + +@support.requires_zlib() +class CompressedZipImportTestCase(UncompressedZipImportTestCase): + compression = ZIP_DEFLATED + + +class BadFileZipImportTestCase(unittest.TestCase): + def assertZipFailure(self, filename): + self.assertRaises(zipimport.ZipImportError, + zipimport.zipimporter, filename) + + def testNoFile(self): + self.assertZipFailure('AdfjdkFJKDFJjdklfjs') + + def testEmptyFilename(self): + self.assertZipFailure('') + + def testBadArgs(self): + self.assertRaises(TypeError, zipimport.zipimporter, None) + self.assertRaises(TypeError, zipimport.zipimporter, TESTMOD, kwd=None) + self.assertRaises(TypeError, zipimport.zipimporter, + list(os.fsencode(TESTMOD))) + + def testFilenameTooLong(self): + self.assertZipFailure('A' * 33000) + + def testEmptyFile(self): + support.unlink(TESTMOD) + support.create_empty_file(TESTMOD) + self.assertZipFailure(TESTMOD) + + def testFileUnreadable(self): + support.unlink(TESTMOD) + fd = os.open(TESTMOD, os.O_CREAT, 000) + try: + os.close(fd) + + with self.assertRaises(zipimport.ZipImportError) as cm: + zipimport.zipimporter(TESTMOD) + finally: + # If we leave "the read-only bit" set on Windows, nothing can + # delete TESTMOD, and later tests suffer bogus failures. + os.chmod(TESTMOD, 0o666) + support.unlink(TESTMOD) + + def testNotZipFile(self): + support.unlink(TESTMOD) + fp = open(TESTMOD, 'w+') + fp.write('a' * 22) + fp.close() + self.assertZipFailure(TESTMOD) + + # XXX: disabled until this works on Big-endian machines + def _testBogusZipFile(self): + support.unlink(TESTMOD) + fp = open(TESTMOD, 'w+') + fp.write(struct.pack('=I', 0x06054B50)) + fp.write('a' * 18) + fp.close() + z = zipimport.zipimporter(TESTMOD) + + try: + self.assertRaises(TypeError, z.find_module, None) + self.assertRaises(TypeError, z.load_module, None) + self.assertRaises(TypeError, z.is_package, None) + self.assertRaises(TypeError, z.get_code, None) + self.assertRaises(TypeError, z.get_data, None) + self.assertRaises(TypeError, z.get_source, None) + + error = zipimport.ZipImportError + self.assertEqual(z.find_module('abc'), None) + + self.assertRaises(error, z.load_module, 'abc') + self.assertRaises(error, z.get_code, 'abc') + self.assertRaises(OSError, z.get_data, 'abc') + self.assertRaises(error, z.get_source, 'abc') + self.assertRaises(error, z.is_package, 'abc') + finally: + zipimport._zip_directory_cache.clear() + + +def tearDownModule(): + support.unlink(TESTMOD) + + +if __name__ == "__main__": + unittest.main() diff --git a/cpython-tests/cp39/test_zipimport_support.py b/cpython-tests/cp39/test_zipimport_support.py new file mode 100644 index 0000000..8856101 --- /dev/null +++ b/cpython-tests/cp39/test_zipimport_support.py @@ -0,0 +1,242 @@ +# This test module covers support in various parts of the standard library +# for working with modules located inside zipfiles +# The tests are centralised in this fashion to make it easy to drop them +# if a platform doesn't support zipimport +import test.support +import os +import os.path +import sys +import textwrap +import zipfile +import zipimport +import doctest +import inspect +import linecache +import unittest +from test.support.script_helper import (spawn_python, kill_python, assert_python_ok, + make_script, make_zip_script) + +verbose = test.support.verbose + +# Library modules covered by this test set +# pdb (Issue 4201) +# inspect (Issue 4223) +# doctest (Issue 4197) + +# Other test modules with zipimport related tests +# test_zipimport (of course!) +# test_cmd_line_script (covers the zipimport support in runpy) + +# Retrieve some helpers from other test cases +from test import (test_doctest, sample_doctest, sample_doctest_no_doctests, + sample_doctest_no_docstrings) + + +def _run_object_doctest(obj, module): + finder = doctest.DocTestFinder(verbose=verbose, recurse=False) + runner = doctest.DocTestRunner(verbose=verbose) + # Use the object's fully qualified name if it has one + # Otherwise, use the module's name + try: + name = "%s.%s" % (obj.__module__, obj.__qualname__) + except AttributeError: + name = module.__name__ + for example in finder.find(obj, name, module): + runner.run(example) + f, t = runner.failures, runner.tries + if f: + raise test.support.TestFailed("%d of %d doctests failed" % (f, t)) + if verbose: + print ('doctest (%s) ... %d tests with zero failures' % (module.__name__, t)) + return f, t + + + +class ZipSupportTests(unittest.TestCase): + # This used to use the ImportHooksBaseTestCase to restore + # the state of the import related information + # in the sys module after each test. However, that restores + # *too much* information and breaks for the invocation + # of test_doctest. So we do our own thing and leave + # sys.modules alone. + # We also clear the linecache and zipimport cache + # just to avoid any bogus errors due to name reuse in the tests + def setUp(self): + linecache.clearcache() + zipimport._zip_directory_cache.clear() + self.path = sys.path[:] + self.meta_path = sys.meta_path[:] + self.path_hooks = sys.path_hooks[:] + sys.path_importer_cache.clear() + + def tearDown(self): + sys.path[:] = self.path + sys.meta_path[:] = self.meta_path + sys.path_hooks[:] = self.path_hooks + sys.path_importer_cache.clear() + + def test_inspect_getsource_issue4223(self): + test_src = "def foo(): pass\n" + with test.support.temp_dir() as d: + init_name = make_script(d, '__init__', test_src) + name_in_zip = os.path.join('zip_pkg', + os.path.basename(init_name)) + zip_name, run_name = make_zip_script(d, 'test_zip', + init_name, name_in_zip) + os.remove(init_name) + sys.path.insert(0, zip_name) + import zip_pkg + try: + self.assertEqual(inspect.getsource(zip_pkg.foo), test_src) + finally: + del sys.modules["zip_pkg"] + + def test_doctest_issue4197(self): + # To avoid having to keep two copies of the doctest module's + # unit tests in sync, this test works by taking the source of + # test_doctest itself, rewriting it a bit to cope with a new + # location, and then throwing it in a zip file to make sure + # everything still works correctly + test_src = inspect.getsource(test_doctest) + test_src = test_src.replace( + "from test import test_doctest", + "import test_zipped_doctest as test_doctest") + test_src = test_src.replace("test.test_doctest", + "test_zipped_doctest") + test_src = test_src.replace("test.sample_doctest", + "sample_zipped_doctest") + # The sample doctest files rewritten to include in the zipped version. + sample_sources = {} + for mod in [sample_doctest, sample_doctest_no_doctests, + sample_doctest_no_docstrings]: + src = inspect.getsource(mod) + src = src.replace("test.test_doctest", "test_zipped_doctest") + # Rewrite the module name so that, for example, + # "test.sample_doctest" becomes "sample_zipped_doctest". + mod_name = mod.__name__.split(".")[-1] + mod_name = mod_name.replace("sample_", "sample_zipped_") + sample_sources[mod_name] = src + + with test.support.temp_dir() as d: + script_name = make_script(d, 'test_zipped_doctest', + test_src) + zip_name, run_name = make_zip_script(d, 'test_zip', + script_name) + with zipfile.ZipFile(zip_name, 'a') as z: + for mod_name, src in sample_sources.items(): + z.writestr(mod_name + ".py", src) + if verbose: + with zipfile.ZipFile(zip_name, 'r') as zip_file: + print ('Contents of %r:' % zip_name) + zip_file.printdir() + os.remove(script_name) + sys.path.insert(0, zip_name) + import test_zipped_doctest + try: + # Some of the doc tests depend on the colocated text files + # which aren't available to the zipped version (the doctest + # module currently requires real filenames for non-embedded + # tests). So we're forced to be selective about which tests + # to run. + # doctest could really use some APIs which take a text + # string or a file object instead of a filename... + known_good_tests = [ + test_zipped_doctest.SampleClass, + test_zipped_doctest.SampleClass.NestedClass, + test_zipped_doctest.SampleClass.NestedClass.__init__, + test_zipped_doctest.SampleClass.__init__, + test_zipped_doctest.SampleClass.a_classmethod, + test_zipped_doctest.SampleClass.a_property, + test_zipped_doctest.SampleClass.a_staticmethod, + test_zipped_doctest.SampleClass.double, + test_zipped_doctest.SampleClass.get, + test_zipped_doctest.SampleNewStyleClass, + test_zipped_doctest.SampleNewStyleClass.__init__, + test_zipped_doctest.SampleNewStyleClass.double, + test_zipped_doctest.SampleNewStyleClass.get, + test_zipped_doctest.sample_func, + test_zipped_doctest.test_DocTest, + test_zipped_doctest.test_DocTestParser, + test_zipped_doctest.test_DocTestRunner.basics, + test_zipped_doctest.test_DocTestRunner.exceptions, + test_zipped_doctest.test_DocTestRunner.option_directives, + test_zipped_doctest.test_DocTestRunner.optionflags, + test_zipped_doctest.test_DocTestRunner.verbose_flag, + test_zipped_doctest.test_Example, + test_zipped_doctest.test_debug, + test_zipped_doctest.test_testsource, + test_zipped_doctest.test_trailing_space_in_test, + test_zipped_doctest.test_DocTestSuite, + test_zipped_doctest.test_DocTestFinder, + ] + # These tests are the ones which need access + # to the data files, so we don't run them + fail_due_to_missing_data_files = [ + test_zipped_doctest.test_DocFileSuite, + test_zipped_doctest.test_testfile, + test_zipped_doctest.test_unittest_reportflags, + ] + + for obj in known_good_tests: + _run_object_doctest(obj, test_zipped_doctest) + finally: + del sys.modules["test_zipped_doctest"] + + def test_doctest_main_issue4197(self): + test_src = textwrap.dedent("""\ + class Test: + ">>> 'line 2'" + pass + + import doctest + doctest.testmod() + """) + pattern = 'File "%s", line 2, in %s' + with test.support.temp_dir() as d: + script_name = make_script(d, 'script', test_src) + rc, out, err = assert_python_ok(script_name) + expected = pattern % (script_name, "__main__.Test") + if verbose: + print ("Expected line", expected) + print ("Got stdout:") + print (ascii(out)) + self.assertIn(expected.encode('utf-8'), out) + zip_name, run_name = make_zip_script(d, "test_zip", + script_name, '__main__.py') + rc, out, err = assert_python_ok(zip_name) + expected = pattern % (run_name, "__main__.Test") + if verbose: + print ("Expected line", expected) + print ("Got stdout:") + print (ascii(out)) + self.assertIn(expected.encode('utf-8'), out) + + def test_pdb_issue4201(self): + test_src = textwrap.dedent("""\ + def f(): + pass + + import pdb + pdb.Pdb(nosigint=True).runcall(f) + """) + with test.support.temp_dir() as d: + script_name = make_script(d, 'script', test_src) + p = spawn_python(script_name) + p.stdin.write(b'l\n') + data = kill_python(p) + # bdb/pdb applies normcase to its filename before displaying + self.assertIn(os.path.normcase(script_name.encode('utf-8')), data) + zip_name, run_name = make_zip_script(d, "test_zip", + script_name, '__main__.py') + p = spawn_python(zip_name) + p.stdin.write(b'l\n') + data = kill_python(p) + # bdb/pdb applies normcase to its filename before displaying + self.assertIn(os.path.normcase(run_name.encode('utf-8')), data) + + +def tearDownModule(): + test.support.reap_children() + +if __name__ == '__main__': + unittest.main() diff --git a/cpython-tests/cp39/zip_cp437_header.zip b/cpython-tests/cp39/zip_cp437_header.zip new file mode 100644 index 0000000000000000000000000000000000000000..f7c6cf170422c48ce1337a4c05843fe66a4b0bbf GIT binary patch literal 270 zcmWIWW@Zs#U}E54IGj1(Hjhth3p)b?11k`V0&!YqPHJ9aZfbmaW=Tf;#QZkBl8Tbz zujP-QGBE_;)G30lGrzP1MI$4V2m@|Qfx3Y}0Zig>LV!1JBS5x8fC7-R0%8=FkcTh5&DbW=z*2 uYc>UI2Du%m9b!Lz?W_zi?FI&&mdJ6*Ca-0n$rA90maKRyZ91 literal 0 HcmV?d00001 diff --git a/cpython-tests/cp39/ziptestdata/README.md b/cpython-tests/cp39/ziptestdata/README.md new file mode 100644 index 0000000..6b9147d --- /dev/null +++ b/cpython-tests/cp39/ziptestdata/README.md @@ -0,0 +1,35 @@ +# Test data for `test_zipfile` + +The test executables in this directory are created manually from header.sh and +the `testdata_module_inside_zip.py` file. You must have infozip's zip utility +installed (`apt install zip` on Debian). + +## Purpose + +These are used to test executable files with an appended zipfile, in a scenario +where the executable is _not_ a Python interpreter itself so our automatic +zipimport machinery (that'd look for `__main__.py`) is not being used. + +## Updating the test executables + +If you update header.sh or the testdata_module_inside_zip.py file, rerun the +commands below. These are expected to be rarely changed, if ever. + +### Standard old format (2.0) zip file + +``` +zip -0 zip2.zip testdata_module_inside_zip.py +cat header.sh zip2.zip >exe_with_zip +rm zip2.zip +``` + +### Modern format (4.5) zip64 file + +Redirecting from stdin forces infozip's zip tool to create a zip64. + +``` +zip -0 zip64.zip +cat header.sh zip64.zip >exe_with_z64 +rm zip64.zip +``` + diff --git a/cpython-tests/cp39/ziptestdata/exe_with_z64 b/cpython-tests/cp39/ziptestdata/exe_with_z64 new file mode 100644 index 0000000000000000000000000000000000000000..82b03cf39d919d9de05866b4f7cdf1dfe47e6eb3 GIT binary patch literal 978 zcmaJ<-EY${5KkEl$xl4ME4rnV(xNp330}q~w6RoFg|_N41e7XrQ)jhEoXByzLbV6} z7bN~nxXZ`b5aOcPclPh+`)uy)&!pO)@qEF01K%5u#vZQ0`QQ{+-#hbQJlMzfN zumhbn*t?s5Bd=_jPG5pq2*m(Jgo_mHo-#sbTHp%FGB+?21c5M360YVDOC^Boi)A8| zaqW`1mIj`)NHXt(_xjvFK6&e598YZ!YZ3l8f{q6rI6U+Qr@^orj6V8rh65&(EY$|m zyw<+SERwNcOz}kI84m>G zSHN@NP(AKCZFVWm;@bWsvo1d0s^NQ(q;qlPXs1m?Of5j_0ahSNH4rM0DoR1B`pzXg zmbq!Q2?j9dhGVD|)zyN}i{}esyQ-xKTZG$#>tt`JC1{4sF91y#s`x7`^Rh*e)YvZy zgkqqkaUCw?O1P{lg45-zR7)d3Et45`xQsPi8a|7~fpf#r#O@xyAC7yz7Yxqdop@t= z+GecTY{GH*C{6^9iZVG|$~dMm;TcwVF6O`^njW)|c@d2ZNMpBKJnC=V?N}s_K0g`u zf>&q1Drr~`txm&wV0p#0b-g#i7nomB!y-wOlGog%8hujhFq@*CrC0V>0$BJLY}9Yu zdAxPoGdZHaQ8}dT$9GygqyF~x9%(2wjr1B?@B4I!vMx6ZdG|^ES=ode_3v$y*}#wR GCH6PMMGy`E literal 0 HcmV?d00001 diff --git a/cpython-tests/cp39/ziptestdata/exe_with_zip b/cpython-tests/cp39/ziptestdata/exe_with_zip new file mode 100644 index 0000000000000000000000000000000000000000..c833cdf9f934b08fc449da077efae44aa9a06f18 GIT binary patch literal 990 zcmah{&2G~`5Ozt6#BxUB0BGt|a)MeHBzh^WP)i*V6_O}*5D+43W3QVP_S)KAHz`pi zcm>WJc^aOC$6(e@N~!o+%den-3;)Sr%Y!Z0+w(d{LAMq3-uf@P z9m3N*lNvI$JbmPO%o9e4pea*14H@ji{DR<>2{Yf&&6LZ;8JC$DI=^T*Ba%xlbR%}U zITKu*!h8w30IGn(BDw1{$&~BKrT>oSEll57hHpZeMQq=ZPSXIfv;d*Is6d=aFi`;) zaRyv0|GCCbxYCWL2?L0zrbu-GbtR)wnZ5)z7h1BgVd6I7ve+xfDrk(z4*+%OisT#$ zRkbMQ68mL{7!IasRE86N#$2)x!D-R6OmfXY6zLc{TyYHxO~(n_b*@}Av|9(SyZyHB z1)agGL$7a-nuOHrbvUS!;zZ!62(4hslf;Y(%~9cqML=USJ$k}b$;JhQk>6X~JFcw~ z%d9)^A9mZpvl9=`=Dly-vourMXb_;{MX9UeQ7N~ZpAY<7R_*(II{NZyIx1$jt(Dau zHOneZ9ejjVI+sG|%rH|rlgP`o7b`AXUNIxrip1vZklyjijR&>AvAb(Xm+RYSv;Bwb WTE+Dm&))IcO#@!RC&c}$ajc&zD=F;& literal 0 HcmV?d00001 diff --git a/cpython-tests/cp39/ziptestdata/header.sh b/cpython-tests/cp39/ziptestdata/header.sh new file mode 100644 index 0000000..52dc91a --- /dev/null +++ b/cpython-tests/cp39/ziptestdata/header.sh @@ -0,0 +1,24 @@ +#!/bin/bash +INTERPRETER_UNDER_TEST="$1" +if [[ ! -x "${INTERPRETER_UNDER_TEST}" ]]; then + echo "Interpreter must be the command line argument." + exit 4 +fi +EXECUTABLE="$0" exec "${INTERPRETER_UNDER_TEST}" -E - < Date: Sat, 5 Oct 2024 11:29:28 +0100 Subject: [PATCH 07/41] rename integration test folders --- cpython-tests/{cp310 => 3.10}/test_zipfile.py | 0 .../{cp310 => 3.10}/test_zipfile64.py | 0 .../{cp310 => 3.10}/test_zipimport.py | 0 .../{cp310 => 3.10}/test_zipimport_support.py | 0 .../{cp310 => 3.10}/zip_cp437_header.zip | Bin cpython-tests/{cp310 => 3.10}/zipdir.zip | Bin .../{cp310 => 3.10}/ziptestdata/README.md | 0 .../{cp310 => 3.10}/ziptestdata/exe_with_z64 | Bin .../{cp310 => 3.10}/ziptestdata/exe_with_zip | Bin .../{cp310 => 3.10}/ziptestdata/header.sh | 0 .../ziptestdata/testdata_module_inside_zip.py | 0 cpython-tests/{cp311 => 3.11}/test_zipfile.py | 434 +++++++++--------- .../{cp311 => 3.11}/test_zipfile64.py | 0 .../{cp311 => 3.11}/test_zipimport.py | 0 .../{cp311 => 3.11}/zip_cp437_header.zip | Bin cpython-tests/{cp311 => 3.11}/zipdir.zip | Bin .../{cp311 => 3.11}/ziptestdata/README.md | 0 .../{cp311 => 3.11}/ziptestdata/exe_with_z64 | Bin .../{cp311 => 3.11}/ziptestdata/exe_with_zip | Bin .../{cp311 => 3.11}/ziptestdata/header.sh | 0 .../ziptestdata/testdata_module_inside_zip.py | 0 .../{cp312 => 3.12}/test_zipfile/__init__.py | 0 .../{cp312 => 3.12}/test_zipfile/__main__.py | 0 .../test_zipfile/_path/__init__.py | 0 .../test_zipfile/_path/_functools.py | 0 .../test_zipfile/_path/_itertools.py | 0 .../test_zipfile/_path/_support.py | 0 .../test_zipfile/_path/_test_params.py | 0 .../test_zipfile/_path/test_complexity.py | 0 .../test_zipfile/_path/test_path.py | 0 .../test_zipfile/_path/write-alpharep.py | 0 .../{cp312 => 3.12}/test_zipfile/test_core.py | 0 .../{cp312 => 3.12}/test_zipfile64.py | 0 .../{cp312 => 3.12}/test_zipimport.py | 0 .../{cp312 => 3.12}/test_zipimport_support.py | 0 .../{cp312 => 3.12}/zip_cp437_header.zip | Bin cpython-tests/{cp312 => 3.12}/zipdir.zip | Bin .../{cp312 => 3.12}/zipdir_backslash.zip | Bin .../{cp312 => 3.12}/ziptestdata/README.md | 0 .../{cp312 => 3.12}/ziptestdata/exe_with_z64 | Bin .../{cp312 => 3.12}/ziptestdata/exe_with_zip | Bin .../{cp312 => 3.12}/ziptestdata/header.sh | 0 .../ziptestdata/testdata_module_inside_zip.py | 0 cpython-tests/{cp38 => 3.8}/test_zipfile.py | 0 cpython-tests/{cp38 => 3.8}/test_zipfile64.py | 0 cpython-tests/{cp38 => 3.8}/test_zipimport.py | 0 .../{cp38 => 3.8}/test_zipimport_support.py | 0 .../{cp38 => 3.8}/zip_cp437_header.zip | Bin cpython-tests/{cp38 => 3.8}/zipdir.zip | Bin .../{cp38 => 3.8}/ziptestdata/README.md | 0 .../{cp38 => 3.8}/ziptestdata/exe_with_z64 | Bin .../{cp38 => 3.8}/ziptestdata/exe_with_zip | Bin .../{cp38 => 3.8}/ziptestdata/header.sh | 0 .../ziptestdata/testdata_module_inside_zip.py | 0 cpython-tests/{cp39 => 3.9}/test_zipfile.py | 0 cpython-tests/{cp39 => 3.9}/test_zipfile64.py | 0 cpython-tests/{cp39 => 3.9}/test_zipimport.py | 0 .../{cp39 => 3.9}/test_zipimport_support.py | 0 .../{cp39 => 3.9}/zip_cp437_header.zip | Bin cpython-tests/{cp39 => 3.9}/zipdir.zip | Bin .../{cp39 => 3.9}/ziptestdata/README.md | 0 .../{cp39 => 3.9}/ziptestdata/exe_with_z64 | Bin .../{cp39 => 3.9}/ziptestdata/exe_with_zip | Bin .../{cp39 => 3.9}/ziptestdata/header.sh | 0 .../ziptestdata/testdata_module_inside_zip.py | 0 cpython-tests/cp311/test_zipimport_support.py | 243 ---------- 66 files changed, 218 insertions(+), 459 deletions(-) rename cpython-tests/{cp310 => 3.10}/test_zipfile.py (100%) rename cpython-tests/{cp310 => 3.10}/test_zipfile64.py (100%) rename cpython-tests/{cp310 => 3.10}/test_zipimport.py (100%) rename cpython-tests/{cp310 => 3.10}/test_zipimport_support.py (100%) rename cpython-tests/{cp310 => 3.10}/zip_cp437_header.zip (100%) rename cpython-tests/{cp310 => 3.10}/zipdir.zip (100%) rename cpython-tests/{cp310 => 3.10}/ziptestdata/README.md (100%) rename cpython-tests/{cp310 => 3.10}/ziptestdata/exe_with_z64 (100%) rename cpython-tests/{cp310 => 3.10}/ziptestdata/exe_with_zip (100%) rename cpython-tests/{cp310 => 3.10}/ziptestdata/header.sh (100%) rename cpython-tests/{cp310 => 3.10}/ziptestdata/testdata_module_inside_zip.py (100%) rename cpython-tests/{cp311 => 3.11}/test_zipfile.py (90%) rename cpython-tests/{cp311 => 3.11}/test_zipfile64.py (100%) rename cpython-tests/{cp311 => 3.11}/test_zipimport.py (100%) rename cpython-tests/{cp311 => 3.11}/zip_cp437_header.zip (100%) rename cpython-tests/{cp311 => 3.11}/zipdir.zip (100%) rename cpython-tests/{cp311 => 3.11}/ziptestdata/README.md (100%) rename cpython-tests/{cp311 => 3.11}/ziptestdata/exe_with_z64 (100%) rename cpython-tests/{cp311 => 3.11}/ziptestdata/exe_with_zip (100%) rename cpython-tests/{cp311 => 3.11}/ziptestdata/header.sh (100%) rename cpython-tests/{cp311 => 3.11}/ziptestdata/testdata_module_inside_zip.py (100%) rename cpython-tests/{cp312 => 3.12}/test_zipfile/__init__.py (100%) rename cpython-tests/{cp312 => 3.12}/test_zipfile/__main__.py (100%) rename cpython-tests/{cp312 => 3.12}/test_zipfile/_path/__init__.py (100%) rename cpython-tests/{cp312 => 3.12}/test_zipfile/_path/_functools.py (100%) rename cpython-tests/{cp312 => 3.12}/test_zipfile/_path/_itertools.py (100%) rename cpython-tests/{cp312 => 3.12}/test_zipfile/_path/_support.py (100%) rename cpython-tests/{cp312 => 3.12}/test_zipfile/_path/_test_params.py (100%) rename cpython-tests/{cp312 => 3.12}/test_zipfile/_path/test_complexity.py (100%) rename cpython-tests/{cp312 => 3.12}/test_zipfile/_path/test_path.py (100%) rename cpython-tests/{cp312 => 3.12}/test_zipfile/_path/write-alpharep.py (100%) rename cpython-tests/{cp312 => 3.12}/test_zipfile/test_core.py (100%) rename cpython-tests/{cp312 => 3.12}/test_zipfile64.py (100%) rename cpython-tests/{cp312 => 3.12}/test_zipimport.py (100%) rename cpython-tests/{cp312 => 3.12}/test_zipimport_support.py (100%) rename cpython-tests/{cp312 => 3.12}/zip_cp437_header.zip (100%) rename cpython-tests/{cp312 => 3.12}/zipdir.zip (100%) rename cpython-tests/{cp312 => 3.12}/zipdir_backslash.zip (100%) rename cpython-tests/{cp312 => 3.12}/ziptestdata/README.md (100%) rename cpython-tests/{cp312 => 3.12}/ziptestdata/exe_with_z64 (100%) rename cpython-tests/{cp312 => 3.12}/ziptestdata/exe_with_zip (100%) rename cpython-tests/{cp312 => 3.12}/ziptestdata/header.sh (100%) rename cpython-tests/{cp312 => 3.12}/ziptestdata/testdata_module_inside_zip.py (100%) rename cpython-tests/{cp38 => 3.8}/test_zipfile.py (100%) rename cpython-tests/{cp38 => 3.8}/test_zipfile64.py (100%) rename cpython-tests/{cp38 => 3.8}/test_zipimport.py (100%) rename cpython-tests/{cp38 => 3.8}/test_zipimport_support.py (100%) rename cpython-tests/{cp38 => 3.8}/zip_cp437_header.zip (100%) rename cpython-tests/{cp38 => 3.8}/zipdir.zip (100%) rename cpython-tests/{cp38 => 3.8}/ziptestdata/README.md (100%) rename cpython-tests/{cp38 => 3.8}/ziptestdata/exe_with_z64 (100%) rename cpython-tests/{cp38 => 3.8}/ziptestdata/exe_with_zip (100%) rename cpython-tests/{cp38 => 3.8}/ziptestdata/header.sh (100%) rename cpython-tests/{cp38 => 3.8}/ziptestdata/testdata_module_inside_zip.py (100%) rename cpython-tests/{cp39 => 3.9}/test_zipfile.py (100%) rename cpython-tests/{cp39 => 3.9}/test_zipfile64.py (100%) rename cpython-tests/{cp39 => 3.9}/test_zipimport.py (100%) rename cpython-tests/{cp39 => 3.9}/test_zipimport_support.py (100%) rename cpython-tests/{cp39 => 3.9}/zip_cp437_header.zip (100%) rename cpython-tests/{cp39 => 3.9}/zipdir.zip (100%) rename cpython-tests/{cp39 => 3.9}/ziptestdata/README.md (100%) rename cpython-tests/{cp39 => 3.9}/ziptestdata/exe_with_z64 (100%) rename cpython-tests/{cp39 => 3.9}/ziptestdata/exe_with_zip (100%) rename cpython-tests/{cp39 => 3.9}/ziptestdata/header.sh (100%) rename cpython-tests/{cp39 => 3.9}/ziptestdata/testdata_module_inside_zip.py (100%) delete mode 100644 cpython-tests/cp311/test_zipimport_support.py diff --git a/cpython-tests/cp310/test_zipfile.py b/cpython-tests/3.10/test_zipfile.py similarity index 100% rename from cpython-tests/cp310/test_zipfile.py rename to cpython-tests/3.10/test_zipfile.py diff --git a/cpython-tests/cp310/test_zipfile64.py b/cpython-tests/3.10/test_zipfile64.py similarity index 100% rename from cpython-tests/cp310/test_zipfile64.py rename to cpython-tests/3.10/test_zipfile64.py diff --git a/cpython-tests/cp310/test_zipimport.py b/cpython-tests/3.10/test_zipimport.py similarity index 100% rename from cpython-tests/cp310/test_zipimport.py rename to cpython-tests/3.10/test_zipimport.py diff --git a/cpython-tests/cp310/test_zipimport_support.py b/cpython-tests/3.10/test_zipimport_support.py similarity index 100% rename from cpython-tests/cp310/test_zipimport_support.py rename to cpython-tests/3.10/test_zipimport_support.py diff --git a/cpython-tests/cp310/zip_cp437_header.zip b/cpython-tests/3.10/zip_cp437_header.zip similarity index 100% rename from cpython-tests/cp310/zip_cp437_header.zip rename to cpython-tests/3.10/zip_cp437_header.zip diff --git a/cpython-tests/cp310/zipdir.zip b/cpython-tests/3.10/zipdir.zip similarity index 100% rename from cpython-tests/cp310/zipdir.zip rename to cpython-tests/3.10/zipdir.zip diff --git a/cpython-tests/cp310/ziptestdata/README.md b/cpython-tests/3.10/ziptestdata/README.md similarity index 100% rename from cpython-tests/cp310/ziptestdata/README.md rename to cpython-tests/3.10/ziptestdata/README.md diff --git a/cpython-tests/cp310/ziptestdata/exe_with_z64 b/cpython-tests/3.10/ziptestdata/exe_with_z64 similarity index 100% rename from cpython-tests/cp310/ziptestdata/exe_with_z64 rename to cpython-tests/3.10/ziptestdata/exe_with_z64 diff --git a/cpython-tests/cp310/ziptestdata/exe_with_zip b/cpython-tests/3.10/ziptestdata/exe_with_zip similarity index 100% rename from cpython-tests/cp310/ziptestdata/exe_with_zip rename to cpython-tests/3.10/ziptestdata/exe_with_zip diff --git a/cpython-tests/cp310/ziptestdata/header.sh b/cpython-tests/3.10/ziptestdata/header.sh similarity index 100% rename from cpython-tests/cp310/ziptestdata/header.sh rename to cpython-tests/3.10/ziptestdata/header.sh diff --git a/cpython-tests/cp310/ziptestdata/testdata_module_inside_zip.py b/cpython-tests/3.10/ziptestdata/testdata_module_inside_zip.py similarity index 100% rename from cpython-tests/cp310/ziptestdata/testdata_module_inside_zip.py rename to cpython-tests/3.10/ziptestdata/testdata_module_inside_zip.py diff --git a/cpython-tests/cp311/test_zipfile.py b/cpython-tests/3.11/test_zipfile.py similarity index 90% rename from cpython-tests/cp311/test_zipfile.py rename to cpython-tests/3.11/test_zipfile.py index f4c11d8..4399863 100644 --- a/cpython-tests/cp311/test_zipfile.py +++ b/cpython-tests/3.11/test_zipfile.py @@ -20,6 +20,8 @@ from tempfile import TemporaryFile from random import randint, random, randbytes +import zipfile2 + from test.support import script_helper from test.support import ( findfile, requires_zlib, requires_bz2, requires_lzma, @@ -65,7 +67,7 @@ def setUp(self): def make_test_archive(self, f, compression, compresslevel=None): kwargs = {'compression': compression, 'compresslevel': compresslevel} # Create the ZIP archive - with zipfile.ZipFile(f, "w", **kwargs) as zipfp: + with zipfile2.ZipFile(f, "w", **kwargs) as zipfp: zipfp.write(TESTFN, "another.name") zipfp.write(TESTFN, TESTFN) zipfp.writestr("strfile", self.data) @@ -77,7 +79,7 @@ def zip_test(self, f, compression, compresslevel=None): self.make_test_archive(f, compression, compresslevel) # Read the ZIP archive - with zipfile.ZipFile(f, "r", compression) as zipfp: + with zipfile2.ZipFile(f, "r", compression) as zipfp: self.assertEqual(zipfp.read(TESTFN), self.data) self.assertEqual(zipfp.read("another.name"), self.data) self.assertEqual(zipfp.read("strfile"), self.data) @@ -135,7 +137,7 @@ def zip_open_test(self, f, compression): self.make_test_archive(f, compression) # Read the ZIP archive - with zipfile.ZipFile(f, "r", compression) as zipfp: + with zipfile2.ZipFile(f, "r", compression) as zipfp: zipdata1 = [] with zipfp.open(TESTFN) as zipopen1: while True: @@ -162,14 +164,14 @@ def test_open(self): def test_open_with_pathlike(self): path = pathlib.Path(TESTFN2) self.zip_open_test(path, self.compression) - with zipfile.ZipFile(path, "r", self.compression) as zipfp: + with zipfile2.ZipFile(path, "r", self.compression) as zipfp: self.assertIsInstance(zipfp.filename, str) def zip_random_open_test(self, f, compression): self.make_test_archive(f, compression) # Read the ZIP archive - with zipfile.ZipFile(f, "r", compression) as zipfp: + with zipfile2.ZipFile(f, "r", compression) as zipfp: zipdata1 = [] with zipfp.open(TESTFN) as zipopen1: while True: @@ -188,7 +190,7 @@ def zip_read1_test(self, f, compression): self.make_test_archive(f, compression) # Read the ZIP archive - with zipfile.ZipFile(f, "r") as zipfp, \ + with zipfile2.ZipFile(f, "r") as zipfp, \ zipfp.open(TESTFN) as zipopen: zipdata = [] while True: @@ -207,7 +209,7 @@ def zip_read1_10_test(self, f, compression): self.make_test_archive(f, compression) # Read the ZIP archive - with zipfile.ZipFile(f, "r") as zipfp, \ + with zipfile2.ZipFile(f, "r") as zipfp, \ zipfp.open(TESTFN) as zipopen: zipdata = [] while True: @@ -227,7 +229,7 @@ def zip_readline_read_test(self, f, compression): self.make_test_archive(f, compression) # Read the ZIP archive - with zipfile.ZipFile(f, "r") as zipfp, \ + with zipfile2.ZipFile(f, "r") as zipfp, \ zipfp.open(TESTFN) as zipopen: data = b'' while True: @@ -252,7 +254,7 @@ def zip_readline_test(self, f, compression): self.make_test_archive(f, compression) # Read the ZIP archive - with zipfile.ZipFile(f, "r") as zipfp: + with zipfile2.ZipFile(f, "r") as zipfp: with zipfp.open(TESTFN) as zipopen: for line in self.line_gen: linedata = zipopen.readline() @@ -266,7 +268,7 @@ def zip_readlines_test(self, f, compression): self.make_test_archive(f, compression) # Read the ZIP archive - with zipfile.ZipFile(f, "r") as zipfp: + with zipfile2.ZipFile(f, "r") as zipfp: with zipfp.open(TESTFN) as zipopen: ziplines = zipopen.readlines() for line, zipline in zip(self.line_gen, ziplines): @@ -280,7 +282,7 @@ def zip_iterlines_test(self, f, compression): self.make_test_archive(f, compression) # Read the ZIP archive - with zipfile.ZipFile(f, "r") as zipfp: + with zipfile2.ZipFile(f, "r") as zipfp: with zipfp.open(TESTFN) as zipopen: for line, zipline in zip(self.line_gen, zipopen): self.assertEqual(zipline, line) @@ -292,23 +294,23 @@ def test_iterlines(self): def test_low_compression(self): """Check for cases where compressed data is larger than original.""" # Create the ZIP archive - with zipfile.ZipFile(TESTFN2, "w", self.compression) as zipfp: + with zipfile2.ZipFile(TESTFN2, "w", self.compression) as zipfp: zipfp.writestr("strfile", '12') # Get an open object for strfile - with zipfile.ZipFile(TESTFN2, "r", self.compression) as zipfp: + with zipfile2.ZipFile(TESTFN2, "r", self.compression) as zipfp: with zipfp.open("strfile") as openobj: self.assertEqual(openobj.read(1), b'1') self.assertEqual(openobj.read(1), b'2') def test_writestr_compression(self): - zipfp = zipfile.ZipFile(TESTFN2, "w") + zipfp = zipfile2.ZipFile(TESTFN2, "w") zipfp.writestr("b.txt", "hello world", compress_type=self.compression) info = zipfp.getinfo('b.txt') self.assertEqual(info.compress_type, self.compression) def test_writestr_compresslevel(self): - zipfp = zipfile.ZipFile(TESTFN2, "w", compresslevel=1) + zipfp = zipfile2.ZipFile(TESTFN2, "w", compresslevel=1) zipfp.writestr("a.txt", "hello world", compress_type=self.compression) zipfp.writestr("b.txt", "hello world", compress_type=self.compression, compresslevel=2) @@ -329,7 +331,7 @@ def test_read_return_size(self): for test_size in (1, 4095, 4096, 4097, 16384): file_size = test_size + 1 junk = randbytes(file_size) - with zipfile.ZipFile(io.BytesIO(), "w", self.compression) as zipf: + with zipfile2.ZipFile(io.BytesIO(), "w", self.compression) as zipf: zipf.writestr('foo', junk) with zipf.open('foo', 'r') as fp: buf = fp.read(test_size) @@ -337,20 +339,20 @@ def test_read_return_size(self): def test_truncated_zipfile(self): fp = io.BytesIO() - with zipfile.ZipFile(fp, mode='w') as zipf: + with zipfile2.ZipFile(fp, mode='w') as zipf: zipf.writestr('strfile', self.data, compress_type=self.compression) end_offset = fp.tell() zipfiledata = fp.getvalue() fp = io.BytesIO(zipfiledata) - with zipfile.ZipFile(fp) as zipf: + with zipfile2.ZipFile(fp) as zipf: with zipf.open('strfile') as zipopen: fp.truncate(end_offset - 20) with self.assertRaises(EOFError): zipopen.read() fp = io.BytesIO(zipfiledata) - with zipfile.ZipFile(fp) as zipf: + with zipfile2.ZipFile(fp) as zipf: with zipf.open('strfile') as zipopen: fp.truncate(end_offset - 20) with self.assertRaises(EOFError): @@ -358,7 +360,7 @@ def test_truncated_zipfile(self): pass fp = io.BytesIO(zipfiledata) - with zipfile.ZipFile(fp) as zipf: + with zipfile2.ZipFile(fp) as zipf: with zipf.open('strfile') as zipopen: fp.truncate(end_offset - 20) with self.assertRaises(EOFError): @@ -368,12 +370,12 @@ def test_truncated_zipfile(self): def test_repr(self): fname = 'file.name' for f in get_files(self): - with zipfile.ZipFile(f, 'w', self.compression) as zipfp: + with zipfile2.ZipFile(f, 'w', self.compression) as zipfp: zipfp.write(TESTFN, fname) r = repr(zipfp) self.assertIn("mode='w'", r) - with zipfile.ZipFile(f, 'r') as zipfp: + with zipfile2.ZipFile(f, 'r') as zipfp: r = repr(zipfp) if isinstance(f, str): self.assertIn('filename=%r' % f, r) @@ -403,7 +405,7 @@ def test_compresslevel_basic(self): def test_per_file_compresslevel(self): """Check that files within a Zip archive can have different compression levels.""" - with zipfile.ZipFile(TESTFN2, "w", compresslevel=1) as zipfp: + with zipfile2.ZipFile(TESTFN2, "w", compresslevel=1) as zipfp: zipfp.write(TESTFN, 'compress_1') zipfp.write(TESTFN, 'compress_9', compresslevel=9) one_info = zipfp.getinfo('compress_1') @@ -425,7 +427,7 @@ def write(self, data): while True: testfile = BrokenFile() count = None - with zipfile.ZipFile(testfile, 'w', self.compression) as zipfp: + with zipfile2.ZipFile(testfile, 'w', self.compression) as zipfp: with zipfp.open('file1', 'w') as f: f.write(b'data1') count = 0 @@ -438,11 +440,11 @@ def write(self, data): break finally: count = None - with zipfile.ZipFile(io.BytesIO(testfile.getvalue())) as zipfp: + with zipfile2.ZipFile(io.BytesIO(testfile.getvalue())) as zipfp: self.assertEqual(zipfp.namelist(), ['file1']) self.assertEqual(zipfp.read('file1'), b'data1') - with zipfile.ZipFile(io.BytesIO(testfile.getvalue())) as zipfp: + with zipfile2.ZipFile(io.BytesIO(testfile.getvalue())) as zipfp: self.assertEqual(zipfp.namelist(), ['file1', 'file2']) self.assertEqual(zipfp.read('file1'), b'data1') self.assertEqual(zipfp.read('file2'), b'data2') @@ -464,7 +466,7 @@ def zip_test_writestr_permissions(self, f, compression): # instance. self.make_test_archive(f, compression) - with zipfile.ZipFile(f, "r") as zipfp: + with zipfile2.ZipFile(f, "r") as zipfp: zinfo = zipfp.getinfo('strfile') self.assertEqual(zinfo.external_attr, 0o600 << 16) @@ -476,18 +478,18 @@ def test_writestr_permissions(self): self.zip_test_writestr_permissions(f, zipfile.ZIP_STORED) def test_absolute_arcnames(self): - with zipfile.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED) as zipfp: + with zipfile2.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED) as zipfp: zipfp.write(TESTFN, "/absolute") - with zipfile.ZipFile(TESTFN2, "r", zipfile.ZIP_STORED) as zipfp: + with zipfile2.ZipFile(TESTFN2, "r", zipfile.ZIP_STORED) as zipfp: self.assertEqual(zipfp.namelist(), ["absolute"]) def test_append_to_zip_file(self): """Test appending to an existing zipfile.""" - with zipfile.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED) as zipfp: + with zipfile2.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED) as zipfp: zipfp.write(TESTFN, TESTFN) - with zipfile.ZipFile(TESTFN2, "a", zipfile.ZIP_STORED) as zipfp: + with zipfile2.ZipFile(TESTFN2, "a", zipfile.ZIP_STORED) as zipfp: zipfp.writestr("strfile", self.data) self.assertEqual(zipfp.namelist(), [TESTFN, "strfile"]) @@ -499,24 +501,24 @@ def test_append_to_non_zip_file(self): with open(TESTFN2, 'wb') as f: f.write(data) - with zipfile.ZipFile(TESTFN2, "a", zipfile.ZIP_STORED) as zipfp: + with zipfile2.ZipFile(TESTFN2, "a", zipfile.ZIP_STORED) as zipfp: zipfp.write(TESTFN, TESTFN) with open(TESTFN2, 'rb') as f: f.seek(len(data)) - with zipfile.ZipFile(f, "r") as zipfp: + with zipfile2.ZipFile(f, "r") as zipfp: self.assertEqual(zipfp.namelist(), [TESTFN]) self.assertEqual(zipfp.read(TESTFN), self.data) with open(TESTFN2, 'rb') as f: self.assertEqual(f.read(len(data)), data) zipfiledata = f.read() - with io.BytesIO(zipfiledata) as bio, zipfile.ZipFile(bio) as zipfp: + with io.BytesIO(zipfiledata) as bio, zipfile2.ZipFile(bio) as zipfp: self.assertEqual(zipfp.namelist(), [TESTFN]) self.assertEqual(zipfp.read(TESTFN), self.data) def test_read_concatenated_zip_file(self): with io.BytesIO() as bio: - with zipfile.ZipFile(bio, 'w', zipfile.ZIP_STORED) as zipfp: + with zipfile2.ZipFile(bio, 'w', zipfile.ZIP_STORED) as zipfp: zipfp.write(TESTFN, TESTFN) zipfiledata = bio.getvalue() data = b'I am not a ZipFile!'*10 @@ -524,13 +526,13 @@ def test_read_concatenated_zip_file(self): f.write(data) f.write(zipfiledata) - with zipfile.ZipFile(TESTFN2) as zipfp: + with zipfile2.ZipFile(TESTFN2) as zipfp: self.assertEqual(zipfp.namelist(), [TESTFN]) self.assertEqual(zipfp.read(TESTFN), self.data) def test_append_to_concatenated_zip_file(self): with io.BytesIO() as bio: - with zipfile.ZipFile(bio, 'w', zipfile.ZIP_STORED) as zipfp: + with zipfile2.ZipFile(bio, 'w', zipfile.ZIP_STORED) as zipfp: zipfp.write(TESTFN, TESTFN) zipfiledata = bio.getvalue() data = b'I am not a ZipFile!'*1000000 @@ -538,50 +540,50 @@ def test_append_to_concatenated_zip_file(self): f.write(data) f.write(zipfiledata) - with zipfile.ZipFile(TESTFN2, 'a') as zipfp: + with zipfile2.ZipFile(TESTFN2, 'a') as zipfp: self.assertEqual(zipfp.namelist(), [TESTFN]) zipfp.writestr('strfile', self.data) with open(TESTFN2, 'rb') as f: self.assertEqual(f.read(len(data)), data) zipfiledata = f.read() - with io.BytesIO(zipfiledata) as bio, zipfile.ZipFile(bio) as zipfp: + with io.BytesIO(zipfiledata) as bio, zipfile2.ZipFile(bio) as zipfp: self.assertEqual(zipfp.namelist(), [TESTFN, 'strfile']) self.assertEqual(zipfp.read(TESTFN), self.data) self.assertEqual(zipfp.read('strfile'), self.data) def test_ignores_newline_at_end(self): - with zipfile.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED) as zipfp: + with zipfile2.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED) as zipfp: zipfp.write(TESTFN, TESTFN) with open(TESTFN2, 'a', encoding='utf-8') as f: f.write("\r\n\00\00\00") - with zipfile.ZipFile(TESTFN2, "r") as zipfp: - self.assertIsInstance(zipfp, zipfile.ZipFile) + with zipfile2.ZipFile(TESTFN2, "r") as zipfp: + self.assertIsInstance(zipfp, zipfile2.ZipFile) def test_ignores_stuff_appended_past_comments(self): - with zipfile.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED) as zipfp: + with zipfile2.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED) as zipfp: zipfp.comment = b"this is a comment" zipfp.write(TESTFN, TESTFN) with open(TESTFN2, 'a', encoding='utf-8') as f: f.write("abcdef\r\n") - with zipfile.ZipFile(TESTFN2, "r") as zipfp: - self.assertIsInstance(zipfp, zipfile.ZipFile) + with zipfile2.ZipFile(TESTFN2, "r") as zipfp: + self.assertIsInstance(zipfp, zipfile2.ZipFile) self.assertEqual(zipfp.comment, b"this is a comment") def test_write_default_name(self): """Check that calling ZipFile.write without arcname specified produces the expected result.""" - with zipfile.ZipFile(TESTFN2, "w") as zipfp: + with zipfile2.ZipFile(TESTFN2, "w") as zipfp: zipfp.write(TESTFN) with open(TESTFN, "rb") as f: self.assertEqual(zipfp.read(TESTFN), f.read()) def test_io_on_closed_zipextfile(self): fname = "somefile.txt" - with zipfile.ZipFile(TESTFN2, mode="w") as zipfp: + with zipfile2.ZipFile(TESTFN2, mode="w") as zipfp: zipfp.writestr(fname, "bogus") - with zipfile.ZipFile(TESTFN2, mode="r") as zipfp: + with zipfile2.ZipFile(TESTFN2, mode="r") as zipfp: with zipfp.open(fname) as fid: fid.close() self.assertRaises(ValueError, fid.read) @@ -593,23 +595,23 @@ def test_io_on_closed_zipextfile(self): def test_write_to_readonly(self): """Check that trying to call write() on a readonly ZipFile object raises a ValueError.""" - with zipfile.ZipFile(TESTFN2, mode="w") as zipfp: + with zipfile2.ZipFile(TESTFN2, mode="w") as zipfp: zipfp.writestr("somefile.txt", "bogus") - with zipfile.ZipFile(TESTFN2, mode="r") as zipfp: + with zipfile2.ZipFile(TESTFN2, mode="r") as zipfp: self.assertRaises(ValueError, zipfp.write, TESTFN) - with zipfile.ZipFile(TESTFN2, mode="r") as zipfp: + with zipfile2.ZipFile(TESTFN2, mode="r") as zipfp: with self.assertRaises(ValueError): zipfp.open(TESTFN, mode='w') def test_add_file_before_1980(self): # Set atime and mtime to 1970-01-01 os.utime(TESTFN, (0, 0)) - with zipfile.ZipFile(TESTFN2, "w") as zipfp: + with zipfile2.ZipFile(TESTFN2, "w") as zipfp: self.assertRaises(ValueError, zipfp.write, TESTFN) - with zipfile.ZipFile(TESTFN2, "w", strict_timestamps=False) as zipfp: + with zipfile2.ZipFile(TESTFN2, "w", strict_timestamps=False) as zipfp: zipfp.write(TESTFN) zinfo = zipfp.getinfo(TESTFN) self.assertEqual(zinfo.date_time, (1980, 1, 1, 0, 0, 0)) @@ -638,10 +640,10 @@ def test_add_file_after_2107(self): # https://bugs.python.org/issue39460#msg360952 self.skipTest(f"Linux VFS/XFS kernel bug detected: {mtime_ns=}") - with zipfile.ZipFile(TESTFN2, "w") as zipfp: + with zipfile2.ZipFile(TESTFN2, "w") as zipfp: self.assertRaises(struct.error, zipfp.write, TESTFN) - with zipfile.ZipFile(TESTFN2, "w", strict_timestamps=False) as zipfp: + with zipfile2.ZipFile(TESTFN2, "w", strict_timestamps=False) as zipfp: zipfp.write(TESTFN) zinfo = zipfp.getinfo(TESTFN) self.assertEqual(zinfo.date_time, (2107, 12, 31, 23, 59, 59)) @@ -655,7 +657,7 @@ class DeflateTestsWithSourceFile(AbstractTestsWithSourceFile, def test_per_file_compression(self): """Check that files within a Zip archive can have different compression options.""" - with zipfile.ZipFile(TESTFN2, "w") as zipfp: + with zipfile2.ZipFile(TESTFN2, "w") as zipfp: zipfp.write(TESTFN, 'storeme', zipfile.ZIP_STORED) zipfp.write(TESTFN, 'deflateme', zipfile.ZIP_DEFLATED) sinfo = zipfp.getinfo('storeme') @@ -696,13 +698,13 @@ def setUp(self): def zip_test(self, f, compression): # Create the ZIP archive - with zipfile.ZipFile(f, "w", compression, allowZip64=True) as zipfp: + with zipfile2.ZipFile(f, "w", compression, allowZip64=True) as zipfp: zipfp.write(TESTFN, "another.name") zipfp.write(TESTFN, TESTFN) zipfp.writestr("strfile", self.data) # Read the ZIP archive - with zipfile.ZipFile(f, "r", compression) as zipfp: + with zipfile2.ZipFile(f, "r", compression) as zipfp: self.assertEqual(zipfp.read(TESTFN), self.data) self.assertEqual(zipfp.read("another.name"), self.data) self.assertEqual(zipfp.read("strfile"), self.data) @@ -758,7 +760,7 @@ def test_basic(self): def test_too_many_files(self): # This test checks that more than 64k files can be added to an archive, # and that the resulting archive can be read properly by ZipFile - zipf = zipfile.ZipFile(TESTFN, "w", self.compression, + zipf = zipfile2.ZipFile(TESTFN, "w", self.compression, allowZip64=True) zipf.debug = 100 numfiles = 15 @@ -767,7 +769,7 @@ def test_too_many_files(self): self.assertEqual(len(zipf.namelist()), numfiles) zipf.close() - zipf2 = zipfile.ZipFile(TESTFN, "r", self.compression) + zipf2 = zipfile2.ZipFile(TESTFN, "r", self.compression) self.assertEqual(len(zipf2.namelist()), numfiles) for i in range(numfiles): content = zipf2.read("foo%08d" % i).decode('ascii') @@ -775,7 +777,7 @@ def test_too_many_files(self): zipf2.close() def test_too_many_files_append(self): - zipf = zipfile.ZipFile(TESTFN, "w", self.compression, + zipf = zipfile2.ZipFile(TESTFN, "w", self.compression, allowZip64=False) zipf.debug = 100 numfiles = 9 @@ -787,7 +789,7 @@ def test_too_many_files_append(self): self.assertEqual(len(zipf.namelist()), numfiles) zipf.close() - zipf = zipfile.ZipFile(TESTFN, "a", self.compression, + zipf = zipfile2.ZipFile(TESTFN, "a", self.compression, allowZip64=False) zipf.debug = 100 self.assertEqual(len(zipf.namelist()), numfiles) @@ -796,7 +798,7 @@ def test_too_many_files_append(self): self.assertEqual(len(zipf.namelist()), numfiles) zipf.close() - zipf = zipfile.ZipFile(TESTFN, "a", self.compression, + zipf = zipfile2.ZipFile(TESTFN, "a", self.compression, allowZip64=True) zipf.debug = 100 self.assertEqual(len(zipf.namelist()), numfiles) @@ -806,7 +808,7 @@ def test_too_many_files_append(self): self.assertEqual(len(zipf.namelist()), numfiles2) zipf.close() - zipf2 = zipfile.ZipFile(TESTFN, "r", self.compression) + zipf2 = zipfile2.ZipFile(TESTFN, "r", self.compression) self.assertEqual(len(zipf2.namelist()), numfiles2) for i in range(numfiles2): content = zipf2.read("foo%08d" % i).decode('ascii') @@ -825,12 +827,12 @@ class StoredTestZip64InSmallFiles(AbstractTestZip64InSmallFiles, compression = zipfile.ZIP_STORED def large_file_exception_test(self, f, compression): - with zipfile.ZipFile(f, "w", compression, allowZip64=False) as zipfp: + with zipfile2.ZipFile(f, "w", compression, allowZip64=False) as zipfp: self.assertRaises(zipfile.LargeZipFile, zipfp.write, TESTFN, "another.name") def large_file_exception_test2(self, f, compression): - with zipfile.ZipFile(f, "w", compression, allowZip64=False) as zipfp: + with zipfile2.ZipFile(f, "w", compression, allowZip64=False) as zipfp: self.assertRaises(zipfile.LargeZipFile, zipfp.writestr, "another.name", self.data) @@ -840,24 +842,24 @@ def test_large_file_exception(self): self.large_file_exception_test2(f, zipfile.ZIP_STORED) def test_absolute_arcnames(self): - with zipfile.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED, + with zipfile2.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED, allowZip64=True) as zipfp: zipfp.write(TESTFN, "/absolute") - with zipfile.ZipFile(TESTFN2, "r", zipfile.ZIP_STORED) as zipfp: + with zipfile2.ZipFile(TESTFN2, "r", zipfile.ZIP_STORED) as zipfp: self.assertEqual(zipfp.namelist(), ["absolute"]) def test_append(self): # Test that appending to the Zip64 archive doesn't change # extra fields of existing entries. - with zipfile.ZipFile(TESTFN2, "w", allowZip64=True) as zipfp: + with zipfile2.ZipFile(TESTFN2, "w", allowZip64=True) as zipfp: zipfp.writestr("strfile", self.data) - with zipfile.ZipFile(TESTFN2, "r", allowZip64=True) as zipfp: + with zipfile2.ZipFile(TESTFN2, "r", allowZip64=True) as zipfp: zinfo = zipfp.getinfo("strfile") extra = zinfo.extra - with zipfile.ZipFile(TESTFN2, "a", allowZip64=True) as zipfp: + with zipfile2.ZipFile(TESTFN2, "a", allowZip64=True) as zipfp: zipfp.writestr("strfile2", self.data) - with zipfile.ZipFile(TESTFN2, "r", allowZip64=True) as zipfp: + with zipfile2.ZipFile(TESTFN2, "r", allowZip64=True) as zipfp: zinfo = zipfp.getinfo("strfile") self.assertEqual(zinfo.extra, extra) @@ -982,7 +984,7 @@ def test_bad_zip64_extra(self): file_size_64_set=True, ) with self.assertRaises(zipfile.BadZipFile) as e: - zipfile.ZipFile(io.BytesIO(missing_file_size_extra)) + zipfile2.ZipFile(io.BytesIO(missing_file_size_extra)) self.assertIn('file size', str(e.exception).lower()) # zip64 file size present, zip64 compress size present, one field in @@ -993,7 +995,7 @@ def test_bad_zip64_extra(self): compress_size_64_set=True, ) with self.assertRaises(zipfile.BadZipFile) as e: - zipfile.ZipFile(io.BytesIO(missing_compress_size_extra)) + zipfile2.ZipFile(io.BytesIO(missing_compress_size_extra)) self.assertIn('compress size', str(e.exception).lower()) # zip64 compress size present, no fields in extra, expecting one, @@ -1002,7 +1004,7 @@ def test_bad_zip64_extra(self): compress_size_64_set=True, ) with self.assertRaises(zipfile.BadZipFile) as e: - zipfile.ZipFile(io.BytesIO(missing_compress_size_extra)) + zipfile2.ZipFile(io.BytesIO(missing_compress_size_extra)) self.assertIn('compress size', str(e.exception).lower()) # zip64 file size present, zip64 compress size present, zip64 header @@ -1016,7 +1018,7 @@ def test_bad_zip64_extra(self): header_offset_64_set=True, ) with self.assertRaises(zipfile.BadZipFile) as e: - zipfile.ZipFile(io.BytesIO(missing_header_offset_extra)) + zipfile2.ZipFile(io.BytesIO(missing_header_offset_extra)) self.assertIn('header offset', str(e.exception).lower()) # zip64 compress size present, zip64 header offset present, one field @@ -1028,7 +1030,7 @@ def test_bad_zip64_extra(self): header_offset_64_set=True, ) with self.assertRaises(zipfile.BadZipFile) as e: - zipfile.ZipFile(io.BytesIO(missing_header_offset_extra)) + zipfile2.ZipFile(io.BytesIO(missing_header_offset_extra)) self.assertIn('header offset', str(e.exception).lower()) # zip64 file size present, zip64 header offset present, one field in @@ -1040,7 +1042,7 @@ def test_bad_zip64_extra(self): header_offset_64_set=True, ) with self.assertRaises(zipfile.BadZipFile) as e: - zipfile.ZipFile(io.BytesIO(missing_header_offset_extra)) + zipfile2.ZipFile(io.BytesIO(missing_header_offset_extra)) self.assertIn('header offset', str(e.exception).lower()) # zip64 header offset present, no fields in extra, expecting one, @@ -1051,7 +1053,7 @@ def test_bad_zip64_extra(self): header_offset_64_set=True, ) with self.assertRaises(zipfile.BadZipFile) as e: - zipfile.ZipFile(io.BytesIO(missing_header_offset_extra)) + zipfile2.ZipFile(io.BytesIO(missing_header_offset_extra)) self.assertIn('header offset', str(e.exception).lower()) def test_generated_valid_zip64_extra(self): @@ -1074,7 +1076,7 @@ def test_generated_valid_zip64_extra(self): kwargs = {} for c in combo: kwargs.update(c) - with zipfile.ZipFile(io.BytesIO(self.make_zip64_file(**kwargs))) as zf: + with zipfile2.ZipFile(io.BytesIO(self.make_zip64_file(**kwargs))) as zf: zinfo = zf.infolist()[0] self.assertEqual(zinfo.file_size, expected_file_size) self.assertEqual(zinfo.compress_size, expected_compress_size) @@ -1105,7 +1107,7 @@ def tearDown(self): def test_close_after_close(self): data = b'content' - with zipfile.ZipFile(TESTFN2, "w", self.compression) as zipf: + with zipfile2.ZipFile(TESTFN2, "w", self.compression) as zipf: w = zipf.open('test', 'w') w.write(data) w.close() @@ -1116,7 +1118,7 @@ def test_close_after_close(self): def test_write_after_close(self): data = b'content' - with zipfile.ZipFile(TESTFN2, "w", self.compression) as zipf: + with zipfile2.ZipFile(TESTFN2, "w", self.compression) as zipf: w = zipf.open('test', 'w') w.write(data) w.close() @@ -1127,7 +1129,7 @@ def test_write_after_close(self): def test_issue44439(self): q = array.array('Q', [1, 2, 3, 4, 5]) LENGTH = len(q) * q.itemsize - with zipfile.ZipFile(io.BytesIO(), 'w', self.compression) as zip: + with zipfile2.ZipFile(io.BytesIO(), 'w', self.compression) as zip: with zip.open('data', 'w') as data: self.assertEqual(data.write(q), LENGTH) self.assertEqual(zip.getinfo('data').file_size, LENGTH) @@ -1344,14 +1346,14 @@ def test_write_pathlike(self): class ExtractTests(unittest.TestCase): def make_test_file(self): - with zipfile.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED) as zipfp: + with zipfile2.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED) as zipfp: for fpath, fdata in SMALL_TEST_DATA: zipfp.writestr(fpath, fdata) def test_extract(self): with temp_cwd(): self.make_test_file() - with zipfile.ZipFile(TESTFN2, "r") as zipfp: + with zipfile2.ZipFile(TESTFN2, "r") as zipfp: for fpath, fdata in SMALL_TEST_DATA: writtenfile = zipfp.extract(fpath) @@ -1369,7 +1371,7 @@ def test_extract(self): def _test_extract_with_target(self, target): self.make_test_file() - with zipfile.ZipFile(TESTFN2, "r") as zipfp: + with zipfile2.ZipFile(TESTFN2, "r") as zipfp: for fpath, fdata in SMALL_TEST_DATA: writtenfile = zipfp.extract(fpath, target) @@ -1397,7 +1399,7 @@ def test_extract_with_target_pathlike(self): def test_extract_all(self): with temp_cwd(): self.make_test_file() - with zipfile.ZipFile(TESTFN2, "r") as zipfp: + with zipfile2.ZipFile(TESTFN2, "r") as zipfp: zipfp.extractall() for fpath, fdata in SMALL_TEST_DATA: outfile = os.path.join(os.getcwd(), fpath) @@ -1409,7 +1411,7 @@ def test_extract_all(self): def _test_extract_all_with_target(self, target): self.make_test_file() - with zipfile.ZipFile(TESTFN2, "r") as zipfp: + with zipfile2.ZipFile(TESTFN2, "r") as zipfp: zipfp.extractall(target) for fpath, fdata in SMALL_TEST_DATA: outfile = os.path.join(target, fpath) @@ -1435,7 +1437,7 @@ def check_file(self, filename, content): self.assertEqual(f.read(), content) def test_sanitize_windows_name(self): - san = zipfile.ZipFile._sanitize_windows_name + san = zipfile2.ZipFile._sanitize_windows_name # Passing pathsep in allows this test to work regardless of platform. self.assertEqual(san(r',,?,C:,foo,bar/z', ','), r'_,C_,foo,bar/z') self.assertEqual(san(r'a\b,ce|f"g?h*i', ','), r'a\b,c_d_e_f_g_h_i') @@ -1492,7 +1494,7 @@ def test_extract_hackers_arcnames_posix_only(self): def _test_extract_hackers_arcnames(self, hacknames): for arcname, fixedname in hacknames: content = b'foobar' + arcname.encode() - with zipfile.ZipFile(TESTFN2, 'w', zipfile.ZIP_STORED) as zipfp: + with zipfile2.ZipFile(TESTFN2, 'w', zipfile.ZIP_STORED) as zipfp: zinfo = zipfile.ZipInfo() # preserve backslashes zinfo.filename = arcname @@ -1503,7 +1505,7 @@ def _test_extract_hackers_arcnames(self, hacknames): targetpath = os.path.join('target', 'subdir', 'subsub') correctfile = os.path.join(targetpath, *fixedname.split('/')) - with zipfile.ZipFile(TESTFN2, 'r') as zipfp: + with zipfile2.ZipFile(TESTFN2, 'r') as zipfp: writtenfile = zipfp.extract(arcname, targetpath) self.assertEqual(writtenfile, correctfile, msg='extract %r: %r != %r' % @@ -1511,21 +1513,21 @@ def _test_extract_hackers_arcnames(self, hacknames): self.check_file(correctfile, content) rmtree('target') - with zipfile.ZipFile(TESTFN2, 'r') as zipfp: + with zipfile2.ZipFile(TESTFN2, 'r') as zipfp: zipfp.extractall(targetpath) self.check_file(correctfile, content) rmtree('target') correctfile = os.path.join(os.getcwd(), *fixedname.split('/')) - with zipfile.ZipFile(TESTFN2, 'r') as zipfp: + with zipfile2.ZipFile(TESTFN2, 'r') as zipfp: writtenfile = zipfp.extract(arcname) self.assertEqual(writtenfile, correctfile, msg="extract %r" % arcname) self.check_file(correctfile, content) rmtree(fixedname.split('/')[0]) - with zipfile.ZipFile(TESTFN2, 'r') as zipfp: + with zipfile2.ZipFile(TESTFN2, 'r') as zipfp: zipfp.extractall() self.check_file(correctfile, content) rmtree(fixedname.split('/')[0]) @@ -1536,13 +1538,13 @@ def _test_extract_hackers_arcnames(self, hacknames): class OtherTests(unittest.TestCase): def test_open_via_zip_info(self): # Create the ZIP archive - with zipfile.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED) as zipfp: + with zipfile2.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED, low_level=True) as zipfp: zipfp.writestr("name", "foo") with self.assertWarns(UserWarning): zipfp.writestr("name", "bar") self.assertEqual(zipfp.namelist(), ["name"] * 2) - with zipfile.ZipFile(TESTFN2, "r") as zipfp: + with zipfile2.ZipFile(TESTFN2, "r", low_level=True) as zipfp: infos = zipfp.infolist() data = b"" for info in infos: @@ -1555,7 +1557,7 @@ def test_open_via_zip_info(self): self.assertIn(data, {b"foobar", b"barfoo"}) def test_writestr_extended_local_header_issue1202(self): - with zipfile.ZipFile(TESTFN2, 'w') as orig_zip: + with zipfile2.ZipFile(TESTFN2, 'w') as orig_zip: for data in 'abcdefghijklmnop': zinfo = zipfile.ZipInfo(data) zinfo.flag_bits |= zipfile._MASK_USE_DATA_DESCRIPTOR # Include an extended local header. @@ -1563,25 +1565,25 @@ def test_writestr_extended_local_header_issue1202(self): def test_close(self): """Check that the zipfile is closed after the 'with' block.""" - with zipfile.ZipFile(TESTFN2, "w") as zipfp: + with zipfile2.ZipFile(TESTFN2, "w") as zipfp: for fpath, fdata in SMALL_TEST_DATA: zipfp.writestr(fpath, fdata) self.assertIsNotNone(zipfp.fp, 'zipfp is not open') self.assertIsNone(zipfp.fp, 'zipfp is not closed') - with zipfile.ZipFile(TESTFN2, "r") as zipfp: + with zipfile2.ZipFile(TESTFN2, "r") as zipfp: self.assertIsNotNone(zipfp.fp, 'zipfp is not open') self.assertIsNone(zipfp.fp, 'zipfp is not closed') def test_close_on_exception(self): """Check that the zipfile is closed if an exception is raised in the 'with' block.""" - with zipfile.ZipFile(TESTFN2, "w") as zipfp: + with zipfile2.ZipFile(TESTFN2, "w") as zipfp: for fpath, fdata in SMALL_TEST_DATA: zipfp.writestr(fpath, fdata) try: - with zipfile.ZipFile(TESTFN2, "r") as zipfp2: + with zipfile2.ZipFile(TESTFN2, "r") as zipfp2: raise zipfile.BadZipFile() except zipfile.BadZipFile: self.assertIsNone(zipfp2.fp, 'zipfp is not closed') @@ -1594,29 +1596,29 @@ def test_unsupported_version(self): b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x01\x00\x00\x00\x00xPK\x05\x06' b'\x00\x00\x00\x00\x01\x00\x01\x00/\x00\x00\x00\x1f\x00\x00\x00\x00\x00') - self.assertRaises(NotImplementedError, zipfile.ZipFile, + self.assertRaises(NotImplementedError, zipfile2.ZipFile, io.BytesIO(data), 'r') @requires_zlib() def test_read_unicode_filenames(self): # bug #10801 fname = findfile('zip_cp437_header.zip') - with zipfile.ZipFile(fname) as zipfp: + with zipfile2.ZipFile(fname) as zipfp: for name in zipfp.namelist(): zipfp.open(name).close() def test_write_unicode_filenames(self): - with zipfile.ZipFile(TESTFN, "w") as zf: + with zipfile2.ZipFile(TESTFN, "w") as zf: zf.writestr("foo.txt", "Test for unicode filename") zf.writestr("\xf6.txt", "Test for unicode filename") self.assertIsInstance(zf.infolist()[0].filename, str) - with zipfile.ZipFile(TESTFN, "r") as zf: + with zipfile2.ZipFile(TESTFN, "r") as zf: self.assertEqual(zf.filelist[0].filename, "foo.txt") self.assertEqual(zf.filelist[1].filename, "\xf6.txt") def test_read_after_write_unicode_filenames(self): - with zipfile.ZipFile(TESTFN2, 'w') as zipfp: + with zipfile2.ZipFile(TESTFN2, 'w') as zipfp: zipfp.writestr('приклад', b'sample') self.assertEqual(zipfp.read('приклад'), b'sample') @@ -1625,11 +1627,11 @@ def test_exclusive_create_zip_file(self): unlink(TESTFN2) filename = 'testfile.txt' content = b'hello, world. this is some content.' - with zipfile.ZipFile(TESTFN2, "x", zipfile.ZIP_STORED) as zipfp: + with zipfile2.ZipFile(TESTFN2, "x", zipfile.ZIP_STORED) as zipfp: zipfp.writestr(filename, content) with self.assertRaises(FileExistsError): - zipfile.ZipFile(TESTFN2, "x", zipfile.ZIP_STORED) - with zipfile.ZipFile(TESTFN2, "r") as zipfp: + zipfile2.ZipFile(TESTFN2, "x", zipfile.ZIP_STORED) + with zipfile2.ZipFile(TESTFN2, "r") as zipfp: self.assertEqual(zipfp.namelist(), [filename]) self.assertEqual(zipfp.read(filename), content) @@ -1641,14 +1643,14 @@ def test_create_non_existent_file_for_append(self): content = b'hello, world. this is some content.' try: - with zipfile.ZipFile(TESTFN, 'a') as zf: + with zipfile2.ZipFile(TESTFN, 'a') as zf: zf.writestr(filename, content) except OSError: self.fail('Could not append data to a non-existent zip file.') self.assertTrue(os.path.exists(TESTFN)) - with zipfile.ZipFile(TESTFN, 'r') as zf: + with zipfile2.ZipFile(TESTFN, 'r') as zf: self.assertEqual(zf.read(filename), content) def test_close_erroneous_file(self): @@ -1662,7 +1664,7 @@ def test_close_erroneous_file(self): with open(TESTFN, "w", encoding="utf-8") as fp: fp.write("this is not a legal zip file\n") try: - zf = zipfile.ZipFile(TESTFN) + zf = zipfile2.ZipFile(TESTFN) except zipfile.BadZipFile: pass @@ -1688,7 +1690,7 @@ def test_damaged_zipfile(self): """Check that zipfiles with missing bytes at the end raise BadZipFile.""" # - Create a valid zip file fp = io.BytesIO() - with zipfile.ZipFile(fp, mode="w") as zipf: + with zipfile2.ZipFile(fp, mode="w") as zipf: zipf.writestr("foo.txt", b"O, for a Muse of Fire!") zipfiledata = fp.getvalue() @@ -1696,12 +1698,12 @@ def test_damaged_zipfile(self): # a BadZipFile exception is raised when we try to open it for N in range(len(zipfiledata)): fp = io.BytesIO(zipfiledata[:N]) - self.assertRaises(zipfile.BadZipFile, zipfile.ZipFile, fp) + self.assertRaises(zipfile.BadZipFile, zipfile2.ZipFile, fp) def test_is_zip_valid_file(self): """Check that is_zipfile() correctly identifies zip files.""" # - passing a filename - with zipfile.ZipFile(TESTFN, mode="w") as zipf: + with zipfile2.ZipFile(TESTFN, mode="w") as zipf: zipf.writestr("foo.txt", b"O, for a Muse of Fire!") self.assertTrue(zipfile.is_zipfile(TESTFN)) @@ -1729,16 +1731,16 @@ def test_non_existent_file_raises_OSError(self): # it is ignored, but the user should be sufficiently annoyed by # the message on the output that regression will be noticed # quickly. - self.assertRaises(OSError, zipfile.ZipFile, TESTFN) + self.assertRaises(OSError, zipfile2.ZipFile, TESTFN) def test_empty_file_raises_BadZipFile(self): f = open(TESTFN, 'w', encoding='utf-8') f.close() - self.assertRaises(zipfile.BadZipFile, zipfile.ZipFile, TESTFN) + self.assertRaises(zipfile.BadZipFile, zipfile2.ZipFile, TESTFN) with open(TESTFN, 'w', encoding='utf-8') as fp: fp.write("short file") - self.assertRaises(zipfile.BadZipFile, zipfile.ZipFile, TESTFN) + self.assertRaises(zipfile.BadZipFile, zipfile2.ZipFile, TESTFN) def test_negative_central_directory_offset_raises_BadZipFile(self): # Zip file containing an empty EOCD record @@ -1749,12 +1751,12 @@ def test_negative_central_directory_offset_raises_BadZipFile(self): for dirsize in 1, 2**32-1: buffer[12:16] = struct.pack(' os.path.getsize(TESTFN)) - with zipfile.ZipFile(TESTFN,mode="r") as zipf: + with zipfile2.ZipFile(TESTFN,mode="r") as zipf: self.assertEqual(zipf.comment, b"shorter comment") def test_unicode_comment(self): - with zipfile.ZipFile(TESTFN, "w", zipfile.ZIP_STORED) as zipf: + with zipfile2.ZipFile(TESTFN, "w", zipfile.ZIP_STORED) as zipf: zipf.writestr("foo.txt", "O, for a Muse of Fire!") with self.assertRaises(TypeError): zipf.comment = "this is an error" def test_change_comment_in_empty_archive(self): - with zipfile.ZipFile(TESTFN, "a", zipfile.ZIP_STORED) as zipf: + with zipfile2.ZipFile(TESTFN, "a", zipfile.ZIP_STORED) as zipf: self.assertFalse(zipf.filelist) zipf.comment = b"this is a comment" - with zipfile.ZipFile(TESTFN, "r") as zipf: + with zipfile2.ZipFile(TESTFN, "r") as zipf: self.assertEqual(zipf.comment, b"this is a comment") def test_change_comment_in_nonempty_archive(self): - with zipfile.ZipFile(TESTFN, "w", zipfile.ZIP_STORED) as zipf: + with zipfile2.ZipFile(TESTFN, "w", zipfile.ZIP_STORED) as zipf: zipf.writestr("foo.txt", "O, for a Muse of Fire!") - with zipfile.ZipFile(TESTFN, "a", zipfile.ZIP_STORED) as zipf: + with zipfile2.ZipFile(TESTFN, "a", zipfile.ZIP_STORED) as zipf: self.assertTrue(zipf.filelist) zipf.comment = b"this is a comment" - with zipfile.ZipFile(TESTFN, "r") as zipf: + with zipfile2.ZipFile(TESTFN, "r") as zipf: self.assertEqual(zipf.comment, b"this is a comment") def test_empty_zipfile(self): # Check that creating a file in 'w' or 'a' mode and closing without # adding any files to the archives creates a valid empty ZIP file - zipf = zipfile.ZipFile(TESTFN, mode="w") + zipf = zipfile2.ZipFile(TESTFN, mode="w") zipf.close() try: - zipf = zipfile.ZipFile(TESTFN, mode="r") + zipf = zipfile2.ZipFile(TESTFN, mode="r") except zipfile.BadZipFile: self.fail("Unable to create empty ZIP file in 'w' mode") - zipf = zipfile.ZipFile(TESTFN, mode="a") + zipf = zipfile2.ZipFile(TESTFN, mode="a") zipf.close() try: - zipf = zipfile.ZipFile(TESTFN, mode="r") + zipf = zipfile2.ZipFile(TESTFN, mode="r") except: self.fail("Unable to create empty ZIP file in 'a' mode") @@ -1937,7 +1939,7 @@ def test_open_empty_file(self): # OSError) f = open(TESTFN, 'w', encoding='utf-8') f.close() - self.assertRaises(zipfile.BadZipFile, zipfile.ZipFile, TESTFN, 'r') + self.assertRaises(zipfile.BadZipFile, zipfile2.ZipFile, TESTFN, 'r') def test_create_zipinfo_before_1980(self): self.assertRaises(ValueError, @@ -1981,7 +1983,7 @@ def test_zipfile_with_short_extra_field(self): b'\x00\x00\x00abc\x00\x00PK\x05\x06\x00\x00\x00\x00' b'\x01\x00\x01\x003\x00\x00\x00%\x00\x00\x00\x00\x00' ) - with zipfile.ZipFile(io.BytesIO(zipdata), 'r') as zipf: + with zipfile2.ZipFile(io.BytesIO(zipdata), 'r') as zipf: # testzip returns the name of the first corrupt file, or None self.assertIsNone(zipf.testzip()) @@ -1990,7 +1992,7 @@ def test_open_conflicting_handles(self): msg1 = b"It's fun to charter an accountant!" msg2 = b"And sail the wide accountant sea" msg3 = b"To find, explore the funds offshore" - with zipfile.ZipFile(TESTFN2, 'w', zipfile.ZIP_STORED) as zipf: + with zipfile2.ZipFile(TESTFN2, 'w', zipfile.ZIP_STORED) as zipf: with zipf.open('foo', mode='w') as w2: w2.write(msg1) with zipf.open('bar', mode='w') as w1: @@ -2008,7 +2010,7 @@ def test_open_conflicting_handles(self): with zipf.open('baz', mode='w') as w2: w2.write(msg3) - with zipfile.ZipFile(TESTFN2, 'r') as zipf: + with zipfile2.ZipFile(TESTFN2, 'r') as zipf: self.assertEqual(zipf.read('foo'), msg1) self.assertEqual(zipf.read('bar'), msg2) self.assertEqual(zipf.read('baz'), msg3) @@ -2019,9 +2021,9 @@ def test_seek_tell(self): txt = b"Where's Bruce?" bloc = txt.find(b"Bruce") # Check seek on a file - with zipfile.ZipFile(TESTFN, "w") as zipf: + with zipfile2.ZipFile(TESTFN, "w") as zipf: zipf.writestr("foo.txt", txt) - with zipfile.ZipFile(TESTFN, "r") as zipf: + with zipfile2.ZipFile(TESTFN, "r") as zipf: with zipf.open("foo.txt", "r") as fp: fp.seek(bloc, os.SEEK_SET) self.assertEqual(fp.tell(), bloc) @@ -2036,9 +2038,9 @@ def test_seek_tell(self): self.assertEqual(fp.tell(), 0) # Check seek on memory file data = io.BytesIO() - with zipfile.ZipFile(data, mode="w") as zipf: + with zipfile2.ZipFile(data, mode="w") as zipf: zipf.writestr("foo.txt", txt) - with zipfile.ZipFile(data, mode="r") as zipf: + with zipfile2.ZipFile(data, mode="r") as zipf: with zipf.open("foo.txt", "r") as fp: fp.seek(bloc, os.SEEK_SET) self.assertEqual(fp.tell(), bloc) @@ -2056,10 +2058,10 @@ def test_seek_tell(self): def test_decompress_without_3rd_party_library(self): data = b'PK\x05\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' zip_file = io.BytesIO(data) - with zipfile.ZipFile(zip_file, 'w', compression=zipfile.ZIP_BZIP2) as zf: + with zipfile2.ZipFile(zip_file, 'w', compression=zipfile.ZIP_BZIP2) as zf: zf.writestr('a.txt', b'a') with mock.patch('zipfile.bz2', None): - with zipfile.ZipFile(zip_file) as zf: + with zipfile2.ZipFile(zip_file) as zf: self.assertRaises(RuntimeError, zf.extract, 'a.txt') def tearDown(self): @@ -2072,7 +2074,7 @@ def test_testzip_with_bad_crc(self): """Tests that files with bad CRCs return their name from testzip.""" zipdata = self.zip_with_bad_crc - with zipfile.ZipFile(io.BytesIO(zipdata), mode="r") as zipf: + with zipfile2.ZipFile(io.BytesIO(zipdata), mode="r") as zipf: # testzip returns the name of the first corrupt file, or None self.assertEqual('afile', zipf.testzip()) @@ -2081,16 +2083,16 @@ def test_read_with_bad_crc(self): zipdata = self.zip_with_bad_crc # Using ZipFile.read() - with zipfile.ZipFile(io.BytesIO(zipdata), mode="r") as zipf: + with zipfile2.ZipFile(io.BytesIO(zipdata), mode="r") as zipf: self.assertRaises(zipfile.BadZipFile, zipf.read, 'afile') # Using ZipExtFile.read() - with zipfile.ZipFile(io.BytesIO(zipdata), mode="r") as zipf: + with zipfile2.ZipFile(io.BytesIO(zipdata), mode="r") as zipf: with zipf.open('afile', 'r') as corrupt_file: self.assertRaises(zipfile.BadZipFile, corrupt_file.read) # Same with small reads (in order to exercise the buffering logic) - with zipfile.ZipFile(io.BytesIO(zipdata), mode="r") as zipf: + with zipfile2.ZipFile(io.BytesIO(zipdata), mode="r") as zipf: with zipf.open('afile', 'r') as corrupt_file: corrupt_file.MIN_READ_SIZE = 2 with self.assertRaises(zipfile.BadZipFile): @@ -2183,10 +2185,10 @@ class DecryptionTests(unittest.TestCase): def setUp(self): with open(TESTFN, "wb") as fp: fp.write(self.data) - self.zip = zipfile.ZipFile(TESTFN, "r") + self.zip = zipfile2.ZipFile(TESTFN, "r") with open(TESTFN2, "wb") as fp: fp.write(self.data2) - self.zip2 = zipfile.ZipFile(TESTFN2, "r") + self.zip2 = zipfile2.ZipFile(TESTFN2, "r") def tearDown(self): self.zip.close() @@ -2288,7 +2290,7 @@ def tearDown(self): def make_test_archive(self, f, compression): # Create the ZIP archive - with zipfile.ZipFile(f, "w", compression) as zipfp: + with zipfile2.ZipFile(f, "w", compression) as zipfp: zipfp.write(TESTFN, "another.name") zipfp.write(TESTFN, TESTFN) @@ -2296,7 +2298,7 @@ def zip_test(self, f, compression): self.make_test_archive(f, compression) # Read the ZIP archive - with zipfile.ZipFile(f, "r", compression) as zipfp: + with zipfile2.ZipFile(f, "r", compression) as zipfp: testdata = zipfp.read(TESTFN) self.assertEqual(len(testdata), len(self.data)) self.assertEqual(testdata, self.data) @@ -2310,7 +2312,7 @@ def zip_open_test(self, f, compression): self.make_test_archive(f, compression) # Read the ZIP archive - with zipfile.ZipFile(f, "r", compression) as zipfp: + with zipfile2.ZipFile(f, "r", compression) as zipfp: zipdata1 = [] with zipfp.open(TESTFN) as zipopen1: while True: @@ -2343,7 +2345,7 @@ def zip_random_open_test(self, f, compression): self.make_test_archive(f, compression) # Read the ZIP archive - with zipfile.ZipFile(f, "r", compression) as zipfp: + with zipfile2.ZipFile(f, "r", compression) as zipfp: zipdata1 = [] with zipfp.open(TESTFN) as zipopen1: while True: @@ -2415,11 +2417,11 @@ def test_writestr(self): f = io.BytesIO() f.write(b'abc') bf = io.BufferedWriter(f) - with zipfile.ZipFile(wrapper(bf), 'w', zipfile.ZIP_STORED) as zipfp: + with zipfile2.ZipFile(wrapper(bf), 'w', zipfile.ZIP_STORED) as zipfp: zipfp.writestr('ones', b'111') zipfp.writestr('twos', b'222') self.assertEqual(f.getvalue()[:5], b'abcPK') - with zipfile.ZipFile(f, mode='r') as zipf: + with zipfile2.ZipFile(f, mode='r') as zipf: with zipf.open('ones') as zopen: self.assertEqual(zopen.read(), b'111') with zipf.open('twos') as zopen: @@ -2431,7 +2433,7 @@ def test_write(self): f = io.BytesIO() f.write(b'abc') bf = io.BufferedWriter(f) - with zipfile.ZipFile(wrapper(bf), 'w', zipfile.ZIP_STORED) as zipfp: + with zipfile2.ZipFile(wrapper(bf), 'w', zipfile.ZIP_STORED) as zipfp: self.addCleanup(unlink, TESTFN) with open(TESTFN, 'wb') as f2: f2.write(b'111') @@ -2440,7 +2442,7 @@ def test_write(self): f2.write(b'222') zipfp.write(TESTFN, 'twos') self.assertEqual(f.getvalue()[:5], b'abcPK') - with zipfile.ZipFile(f, mode='r') as zipf: + with zipfile2.ZipFile(f, mode='r') as zipf: with zipf.open('ones') as zopen: self.assertEqual(zopen.read(), b'111') with zipf.open('twos') as zopen: @@ -2452,13 +2454,13 @@ def test_open_write(self): f = io.BytesIO() f.write(b'abc') bf = io.BufferedWriter(f) - with zipfile.ZipFile(wrapper(bf), 'w', zipfile.ZIP_STORED) as zipf: + with zipfile2.ZipFile(wrapper(bf), 'w', zipfile.ZIP_STORED) as zipf: with zipf.open('ones', 'w') as zopen: zopen.write(b'111') with zipf.open('twos', 'w') as zopen: zopen.write(b'222') self.assertEqual(f.getvalue()[:5], b'abcPK') - with zipfile.ZipFile(f) as zipf: + with zipfile2.ZipFile(f) as zipf: self.assertEqual(zipf.read('ones'), b'111') self.assertEqual(zipf.read('twos'), b'222') @@ -2472,7 +2474,7 @@ def setUpClass(cls): def make_test_archive(self, f): # Create the ZIP archive - with zipfile.ZipFile(f, "w", zipfile.ZIP_DEFLATED) as zipfp: + with zipfile2.ZipFile(f, "w", zipfile.ZIP_DEFLATED) as zipfp: zipfp.writestr('ones', self.data1) zipfp.writestr('twos', self.data2) @@ -2481,7 +2483,7 @@ def test_same_file(self): # multiple open() calls can be made without interfering with each other. for f in get_files(self): self.make_test_archive(f) - with zipfile.ZipFile(f, mode="r") as zipf: + with zipfile2.ZipFile(f, mode="r") as zipf: with zipf.open('ones') as zopen1, zipf.open('ones') as zopen2: data1 = zopen1.read(500) data2 = zopen2.read(500) @@ -2495,7 +2497,7 @@ def test_different_file(self): # multiple open() calls can be made without interfering with each other. for f in get_files(self): self.make_test_archive(f) - with zipfile.ZipFile(f, mode="r") as zipf: + with zipfile2.ZipFile(f, mode="r") as zipf: with zipf.open('ones') as zopen1, zipf.open('twos') as zopen2: data1 = zopen1.read(500) data2 = zopen2.read(500) @@ -2509,7 +2511,7 @@ def test_interleaved(self): # multiple open() calls can be made without interfering with each other. for f in get_files(self): self.make_test_archive(f) - with zipfile.ZipFile(f, mode="r") as zipf: + with zipfile2.ZipFile(f, mode="r") as zipf: with zipf.open('ones') as zopen1: data1 = zopen1.read(500) with zipf.open('twos') as zopen2: @@ -2523,7 +2525,7 @@ def test_read_after_close(self): for f in get_files(self): self.make_test_archive(f) with contextlib.ExitStack() as stack: - with zipfile.ZipFile(f, 'r') as zipf: + with zipfile2.ZipFile(f, 'r') as zipf: zopen1 = stack.enter_context(zipf.open('ones')) zopen2 = stack.enter_context(zipf.open('twos')) data1 = zopen1.read(500) @@ -2535,13 +2537,13 @@ def test_read_after_close(self): def test_read_after_write(self): for f in get_files(self): - with zipfile.ZipFile(f, 'w', zipfile.ZIP_DEFLATED) as zipf: + with zipfile2.ZipFile(f, 'w', zipfile.ZIP_DEFLATED) as zipf: zipf.writestr('ones', self.data1) zipf.writestr('twos', self.data2) with zipf.open('ones') as zopen1: data1 = zopen1.read(500) self.assertEqual(data1, self.data1[:500]) - with zipfile.ZipFile(f, 'r') as zipf: + with zipfile2.ZipFile(f, 'r') as zipf: data1 = zipf.read('ones') data2 = zipf.read('twos') self.assertEqual(data1, self.data1) @@ -2549,12 +2551,12 @@ def test_read_after_write(self): def test_write_after_read(self): for f in get_files(self): - with zipfile.ZipFile(f, "w", zipfile.ZIP_DEFLATED) as zipf: + with zipfile2.ZipFile(f, "w", zipfile.ZIP_DEFLATED) as zipf: zipf.writestr('ones', self.data1) with zipf.open('ones') as zopen1: zopen1.read(500) zipf.writestr('twos', self.data2) - with zipfile.ZipFile(f, 'r') as zipf: + with zipfile2.ZipFile(f, 'r') as zipf: data1 = zipf.read('ones') data2 = zipf.read('twos') self.assertEqual(data1, self.data1) @@ -2565,7 +2567,7 @@ def test_many_opens(self): # and don't rely on the garbage collector to free resources. startcount = fd_count() self.make_test_archive(TESTFN2) - with zipfile.ZipFile(TESTFN2, mode="r") as zipf: + with zipfile2.ZipFile(TESTFN2, mode="r") as zipf: for x in range(100): zipf.read('ones') with zipf.open('ones') as zopen1: @@ -2573,16 +2575,16 @@ def test_many_opens(self): self.assertEqual(startcount, fd_count()) def test_write_while_reading(self): - with zipfile.ZipFile(TESTFN2, 'w', zipfile.ZIP_DEFLATED) as zipf: + with zipfile2.ZipFile(TESTFN2, 'w', zipfile.ZIP_DEFLATED) as zipf: zipf.writestr('ones', self.data1) - with zipfile.ZipFile(TESTFN2, 'a', zipfile.ZIP_DEFLATED) as zipf: + with zipfile2.ZipFile(TESTFN2, 'a', zipfile.ZIP_DEFLATED) as zipf: with zipf.open('ones', 'r') as r1: data1 = r1.read(500) with zipf.open('twos', 'w') as w1: w1.write(self.data2) data1 += r1.read() self.assertEqual(data1, self.data1) - with zipfile.ZipFile(TESTFN2) as zipf: + with zipfile2.ZipFile(TESTFN2) as zipf: self.assertEqual(zipf.read('twos'), self.data2) def tearDown(self): @@ -2594,7 +2596,7 @@ def setUp(self): os.mkdir(TESTFN2) def test_extract_dir(self): - with zipfile.ZipFile(findfile("zipdir.zip")) as zipf: + with zipfile2.ZipFile(findfile("zipdir.zip")) as zipf: zipf.extractall(TESTFN2) self.assertTrue(os.path.isdir(os.path.join(TESTFN2, "a"))) self.assertTrue(os.path.isdir(os.path.join(TESTFN2, "a", "b"))) @@ -2609,7 +2611,7 @@ def test_write_dir(self): dirpath = os.path.join(TESTFN2, "x") os.mkdir(dirpath) mode = os.stat(dirpath).st_mode & 0xFFFF - with zipfile.ZipFile(TESTFN, "w") as zipf: + with zipfile2.ZipFile(TESTFN, "w") as zipf: zipf.write(dirpath) zinfo = zipf.filelist[0] self.assertTrue(zinfo.filename.endswith("/x/")) @@ -2618,7 +2620,7 @@ def test_write_dir(self): zinfo = zipf.filelist[1] self.assertTrue(zinfo.filename, "y/") self.assertEqual(zinfo.external_attr, (mode << 16) | 0x10) - with zipfile.ZipFile(TESTFN, "r") as zipf: + with zipfile2.ZipFile(TESTFN, "r") as zipf: zinfo = zipf.filelist[0] self.assertTrue(zinfo.filename.endswith("/x/")) self.assertEqual(zinfo.external_attr, (mode << 16) | 0x10) @@ -2633,12 +2635,12 @@ def test_write_dir(self): def test_writestr_dir(self): os.mkdir(os.path.join(TESTFN2, "x")) - with zipfile.ZipFile(TESTFN, "w") as zipf: + with zipfile2.ZipFile(TESTFN, "w") as zipf: zipf.writestr("x/", b'') zinfo = zipf.filelist[0] self.assertEqual(zinfo.filename, "x/") self.assertEqual(zinfo.external_attr, (0o40775 << 16) | 0x10) - with zipfile.ZipFile(TESTFN, "r") as zipf: + with zipfile2.ZipFile(TESTFN, "r") as zipf: zinfo = zipf.filelist[0] self.assertTrue(zinfo.filename.endswith("x/")) self.assertEqual(zinfo.external_attr, (0o40775 << 16) | 0x10) @@ -2649,7 +2651,7 @@ def test_writestr_dir(self): self.assertEqual(os.listdir(target), ["x"]) def test_mkdir(self): - with zipfile.ZipFile(TESTFN, "w") as zf: + with zipfile2.ZipFile(TESTFN, "w") as zf: zf.mkdir("directory") zinfo = zf.filelist[0] self.assertEqual(zinfo.filename, "directory/") @@ -2681,7 +2683,7 @@ def test_mkdir(self): self.assertEqual(set(os.listdir(target)), {"directory", "directory2", "directory3", "directory4"}) def test_create_directory_with_write(self): - with zipfile.ZipFile(TESTFN, "w") as zf: + with zipfile2.ZipFile(TESTFN, "w") as zf: zf.writestr(zipfile.ZipInfo('directory/'), '') zinfo = zf.filelist[0] @@ -2774,7 +2776,7 @@ def test_test_command(self): def test_list_command(self): zip_name = findfile('zipdir.zip') t = io.StringIO() - with zipfile.ZipFile(zip_name, 'r') as tf: + with zipfile2.ZipFile(zip_name, 'r') as tf: tf.printdir(t) expected = t.getvalue().encode('ascii', 'backslashreplace') for opt in '-l', '--list': @@ -2797,7 +2799,7 @@ def test_create_command(self): try: out = self.zipfilecmd(opt, TESTFN2, *files) self.assertEqual(out, b'') - with zipfile.ZipFile(TESTFN2) as zf: + with zipfile2.ZipFile(TESTFN2) as zf: self.assertEqual(zf.namelist(), namelist) self.assertEqual(zf.read(namelist[0]), b'test 1') self.assertEqual(zf.read(namelist[2]), b'test 2') @@ -2810,7 +2812,7 @@ def test_extract_command(self): with temp_dir() as extdir: out = self.zipfilecmd(opt, zip_name, extdir) self.assertEqual(out, b'') - with zipfile.ZipFile(zip_name) as zf: + with zipfile2.ZipFile(zip_name) as zf: for zi in zf.infolist(): path = os.path.join(extdir, zi.filename.replace('/', os.sep)) @@ -2834,7 +2836,7 @@ def _test_zip_works(self, name): self.assertTrue(zipfile.is_zipfile(name), f'is_zipfile failed on {name}') # Ensure we can operate on these via ZipFile. - with zipfile.ZipFile(name) as zipfp: + with zipfile2.ZipFile(name) as zipfp: for n in zipfp.namelist(): data = zipfp.read(n) self.assertIn(b'FAVORITE_NUMBER', data) @@ -2919,7 +2921,7 @@ def build_alpharep_fixture(): "rep" because it's a representative example """ data = io.BytesIO() - zf = zipfile.ZipFile(data, "w") + zf = zipfile2.ZipFile(data, "w") zf.writestr("a.txt", b"content of a") zf.writestr("b/c.txt", b"content of c") zf.writestr("b/d/e.txt", b"content of e") @@ -3012,7 +3014,7 @@ 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')) + zf = zipfile.Path(zipfile2.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', encoding="utf-8") as strm: @@ -3135,7 +3137,7 @@ def test_mutability(self, alpharep): def huge_zipfile(self): """Create a read-only zipfile with a huge number of entries entries.""" strm = io.BytesIO() - zf = zipfile.ZipFile(strm, "w") + zf = zipfile2.ZipFile(strm, "w") for entry in map(str, range(self.HUGE_ZIPFILE_NUM_ENTRIES)): zf.writestr(entry, entry) zf.mode = 'r' @@ -3160,7 +3162,7 @@ def test_implied_dirs_performance(self): @pass_alpharep def test_read_does_not_close(self, alpharep): alpharep = self.zipfile_ondisk(alpharep) - with zipfile.ZipFile(alpharep) as file: + with zipfile2.ZipFile(alpharep) as file: for rep in range(2): zipfile.Path(file, 'a.txt').read_text(encoding="utf-8") @@ -3294,7 +3296,7 @@ def setUp(self): # not otherwise contained in the zip file. # Data elements are encoded bytes (ascii, utf-8, shift_jis). placeholders = ["n1", "n2"] + self.file_names[2:] - with zipfile.ZipFile(TESTFN, mode="w") as tf: + with zipfile2.ZipFile(TESTFN, mode="w") as tf: for temp, content in zip(placeholders, self.file_content): tf.writestr(temp, content, zipfile.ZIP_STORED) # Hack in the Shift JIS names with flag bit 11 (UTF-8) unset. @@ -3325,30 +3327,30 @@ def _test_read(self, zipfp, expected_names, expected_content): def test_read_with_metadata_encoding(self): # Read the ZIP archive with correct metadata_encoding - with zipfile.ZipFile(TESTFN, "r", metadata_encoding='shift_jis') as zipfp: + with zipfile2.ZipFile(TESTFN, "r", metadata_encoding='shift_jis') as zipfp: self._test_read(zipfp, self.file_names, self.file_content) def test_read_without_metadata_encoding(self): # Read the ZIP archive without metadata_encoding expected_names = [name.encode('shift_jis').decode('cp437') for name in self.file_names[:2]] + self.file_names[2:] - with zipfile.ZipFile(TESTFN, "r") as zipfp: + with zipfile2.ZipFile(TESTFN, "r") as zipfp: self._test_read(zipfp, expected_names, self.file_content) def test_read_with_incorrect_metadata_encoding(self): # Read the ZIP archive with incorrect metadata_encoding expected_names = [name.encode('shift_jis').decode('koi8-u') for name in self.file_names[:2]] + self.file_names[2:] - with zipfile.ZipFile(TESTFN, "r", metadata_encoding='koi8-u') as zipfp: + with zipfile2.ZipFile(TESTFN, "r", metadata_encoding='koi8-u') as zipfp: self._test_read(zipfp, expected_names, self.file_content) def test_read_with_unsuitable_metadata_encoding(self): # Read the ZIP archive with metadata_encoding unsuitable for # decoding metadata with self.assertRaises(UnicodeDecodeError): - zipfile.ZipFile(TESTFN, "r", metadata_encoding='ascii') + zipfile2.ZipFile(TESTFN, "r", metadata_encoding='ascii') with self.assertRaises(UnicodeDecodeError): - zipfile.ZipFile(TESTFN, "r", metadata_encoding='utf-8') + zipfile2.ZipFile(TESTFN, "r", metadata_encoding='utf-8') def test_read_after_append(self): newname = '\u56db' # Han 'four' @@ -3357,14 +3359,14 @@ def test_read_after_append(self): expected_names.append(newname) expected_content = (*self.file_content, b"newcontent") - with zipfile.ZipFile(TESTFN, "a") as zipfp: + with zipfile2.ZipFile(TESTFN, "a") as zipfp: zipfp.writestr(newname, "newcontent") self.assertEqual(sorted(zipfp.namelist()), sorted(expected_names)) - with zipfile.ZipFile(TESTFN, "r") as zipfp: + with zipfile2.ZipFile(TESTFN, "r") as zipfp: self._test_read(zipfp, expected_names, expected_content) - with zipfile.ZipFile(TESTFN, "r", metadata_encoding='shift_jis') as zipfp: + with zipfile2.ZipFile(TESTFN, "r", metadata_encoding='shift_jis') as zipfp: self.assertEqual(sorted(zipfp.namelist()), sorted(expected_names)) for i, (name, content) in enumerate(zip(expected_names, expected_content)): info = zipfp.getinfo(name) @@ -3377,7 +3379,7 @@ def test_read_after_append(self): self.assertEqual(zipfp.read(name), content) def test_write_with_metadata_encoding(self): - ZF = zipfile.ZipFile + ZF = zipfile2.ZipFile for mode in ("w", "x", "a"): with self.assertRaisesRegex(ValueError, "^metadata_encoding is only"): diff --git a/cpython-tests/cp311/test_zipfile64.py b/cpython-tests/3.11/test_zipfile64.py similarity index 100% rename from cpython-tests/cp311/test_zipfile64.py rename to cpython-tests/3.11/test_zipfile64.py diff --git a/cpython-tests/cp311/test_zipimport.py b/cpython-tests/3.11/test_zipimport.py similarity index 100% rename from cpython-tests/cp311/test_zipimport.py rename to cpython-tests/3.11/test_zipimport.py diff --git a/cpython-tests/cp311/zip_cp437_header.zip b/cpython-tests/3.11/zip_cp437_header.zip similarity index 100% rename from cpython-tests/cp311/zip_cp437_header.zip rename to cpython-tests/3.11/zip_cp437_header.zip diff --git a/cpython-tests/cp311/zipdir.zip b/cpython-tests/3.11/zipdir.zip similarity index 100% rename from cpython-tests/cp311/zipdir.zip rename to cpython-tests/3.11/zipdir.zip diff --git a/cpython-tests/cp311/ziptestdata/README.md b/cpython-tests/3.11/ziptestdata/README.md similarity index 100% rename from cpython-tests/cp311/ziptestdata/README.md rename to cpython-tests/3.11/ziptestdata/README.md diff --git a/cpython-tests/cp311/ziptestdata/exe_with_z64 b/cpython-tests/3.11/ziptestdata/exe_with_z64 similarity index 100% rename from cpython-tests/cp311/ziptestdata/exe_with_z64 rename to cpython-tests/3.11/ziptestdata/exe_with_z64 diff --git a/cpython-tests/cp311/ziptestdata/exe_with_zip b/cpython-tests/3.11/ziptestdata/exe_with_zip similarity index 100% rename from cpython-tests/cp311/ziptestdata/exe_with_zip rename to cpython-tests/3.11/ziptestdata/exe_with_zip diff --git a/cpython-tests/cp311/ziptestdata/header.sh b/cpython-tests/3.11/ziptestdata/header.sh similarity index 100% rename from cpython-tests/cp311/ziptestdata/header.sh rename to cpython-tests/3.11/ziptestdata/header.sh diff --git a/cpython-tests/cp311/ziptestdata/testdata_module_inside_zip.py b/cpython-tests/3.11/ziptestdata/testdata_module_inside_zip.py similarity index 100% rename from cpython-tests/cp311/ziptestdata/testdata_module_inside_zip.py rename to cpython-tests/3.11/ziptestdata/testdata_module_inside_zip.py diff --git a/cpython-tests/cp312/test_zipfile/__init__.py b/cpython-tests/3.12/test_zipfile/__init__.py similarity index 100% rename from cpython-tests/cp312/test_zipfile/__init__.py rename to cpython-tests/3.12/test_zipfile/__init__.py diff --git a/cpython-tests/cp312/test_zipfile/__main__.py b/cpython-tests/3.12/test_zipfile/__main__.py similarity index 100% rename from cpython-tests/cp312/test_zipfile/__main__.py rename to cpython-tests/3.12/test_zipfile/__main__.py diff --git a/cpython-tests/cp312/test_zipfile/_path/__init__.py b/cpython-tests/3.12/test_zipfile/_path/__init__.py similarity index 100% rename from cpython-tests/cp312/test_zipfile/_path/__init__.py rename to cpython-tests/3.12/test_zipfile/_path/__init__.py diff --git a/cpython-tests/cp312/test_zipfile/_path/_functools.py b/cpython-tests/3.12/test_zipfile/_path/_functools.py similarity index 100% rename from cpython-tests/cp312/test_zipfile/_path/_functools.py rename to cpython-tests/3.12/test_zipfile/_path/_functools.py diff --git a/cpython-tests/cp312/test_zipfile/_path/_itertools.py b/cpython-tests/3.12/test_zipfile/_path/_itertools.py similarity index 100% rename from cpython-tests/cp312/test_zipfile/_path/_itertools.py rename to cpython-tests/3.12/test_zipfile/_path/_itertools.py diff --git a/cpython-tests/cp312/test_zipfile/_path/_support.py b/cpython-tests/3.12/test_zipfile/_path/_support.py similarity index 100% rename from cpython-tests/cp312/test_zipfile/_path/_support.py rename to cpython-tests/3.12/test_zipfile/_path/_support.py diff --git a/cpython-tests/cp312/test_zipfile/_path/_test_params.py b/cpython-tests/3.12/test_zipfile/_path/_test_params.py similarity index 100% rename from cpython-tests/cp312/test_zipfile/_path/_test_params.py rename to cpython-tests/3.12/test_zipfile/_path/_test_params.py diff --git a/cpython-tests/cp312/test_zipfile/_path/test_complexity.py b/cpython-tests/3.12/test_zipfile/_path/test_complexity.py similarity index 100% rename from cpython-tests/cp312/test_zipfile/_path/test_complexity.py rename to cpython-tests/3.12/test_zipfile/_path/test_complexity.py diff --git a/cpython-tests/cp312/test_zipfile/_path/test_path.py b/cpython-tests/3.12/test_zipfile/_path/test_path.py similarity index 100% rename from cpython-tests/cp312/test_zipfile/_path/test_path.py rename to cpython-tests/3.12/test_zipfile/_path/test_path.py diff --git a/cpython-tests/cp312/test_zipfile/_path/write-alpharep.py b/cpython-tests/3.12/test_zipfile/_path/write-alpharep.py similarity index 100% rename from cpython-tests/cp312/test_zipfile/_path/write-alpharep.py rename to cpython-tests/3.12/test_zipfile/_path/write-alpharep.py diff --git a/cpython-tests/cp312/test_zipfile/test_core.py b/cpython-tests/3.12/test_zipfile/test_core.py similarity index 100% rename from cpython-tests/cp312/test_zipfile/test_core.py rename to cpython-tests/3.12/test_zipfile/test_core.py diff --git a/cpython-tests/cp312/test_zipfile64.py b/cpython-tests/3.12/test_zipfile64.py similarity index 100% rename from cpython-tests/cp312/test_zipfile64.py rename to cpython-tests/3.12/test_zipfile64.py diff --git a/cpython-tests/cp312/test_zipimport.py b/cpython-tests/3.12/test_zipimport.py similarity index 100% rename from cpython-tests/cp312/test_zipimport.py rename to cpython-tests/3.12/test_zipimport.py diff --git a/cpython-tests/cp312/test_zipimport_support.py b/cpython-tests/3.12/test_zipimport_support.py similarity index 100% rename from cpython-tests/cp312/test_zipimport_support.py rename to cpython-tests/3.12/test_zipimport_support.py diff --git a/cpython-tests/cp312/zip_cp437_header.zip b/cpython-tests/3.12/zip_cp437_header.zip similarity index 100% rename from cpython-tests/cp312/zip_cp437_header.zip rename to cpython-tests/3.12/zip_cp437_header.zip diff --git a/cpython-tests/cp312/zipdir.zip b/cpython-tests/3.12/zipdir.zip similarity index 100% rename from cpython-tests/cp312/zipdir.zip rename to cpython-tests/3.12/zipdir.zip diff --git a/cpython-tests/cp312/zipdir_backslash.zip b/cpython-tests/3.12/zipdir_backslash.zip similarity index 100% rename from cpython-tests/cp312/zipdir_backslash.zip rename to cpython-tests/3.12/zipdir_backslash.zip diff --git a/cpython-tests/cp312/ziptestdata/README.md b/cpython-tests/3.12/ziptestdata/README.md similarity index 100% rename from cpython-tests/cp312/ziptestdata/README.md rename to cpython-tests/3.12/ziptestdata/README.md diff --git a/cpython-tests/cp312/ziptestdata/exe_with_z64 b/cpython-tests/3.12/ziptestdata/exe_with_z64 similarity index 100% rename from cpython-tests/cp312/ziptestdata/exe_with_z64 rename to cpython-tests/3.12/ziptestdata/exe_with_z64 diff --git a/cpython-tests/cp312/ziptestdata/exe_with_zip b/cpython-tests/3.12/ziptestdata/exe_with_zip similarity index 100% rename from cpython-tests/cp312/ziptestdata/exe_with_zip rename to cpython-tests/3.12/ziptestdata/exe_with_zip diff --git a/cpython-tests/cp312/ziptestdata/header.sh b/cpython-tests/3.12/ziptestdata/header.sh similarity index 100% rename from cpython-tests/cp312/ziptestdata/header.sh rename to cpython-tests/3.12/ziptestdata/header.sh diff --git a/cpython-tests/cp312/ziptestdata/testdata_module_inside_zip.py b/cpython-tests/3.12/ziptestdata/testdata_module_inside_zip.py similarity index 100% rename from cpython-tests/cp312/ziptestdata/testdata_module_inside_zip.py rename to cpython-tests/3.12/ziptestdata/testdata_module_inside_zip.py diff --git a/cpython-tests/cp38/test_zipfile.py b/cpython-tests/3.8/test_zipfile.py similarity index 100% rename from cpython-tests/cp38/test_zipfile.py rename to cpython-tests/3.8/test_zipfile.py diff --git a/cpython-tests/cp38/test_zipfile64.py b/cpython-tests/3.8/test_zipfile64.py similarity index 100% rename from cpython-tests/cp38/test_zipfile64.py rename to cpython-tests/3.8/test_zipfile64.py diff --git a/cpython-tests/cp38/test_zipimport.py b/cpython-tests/3.8/test_zipimport.py similarity index 100% rename from cpython-tests/cp38/test_zipimport.py rename to cpython-tests/3.8/test_zipimport.py diff --git a/cpython-tests/cp38/test_zipimport_support.py b/cpython-tests/3.8/test_zipimport_support.py similarity index 100% rename from cpython-tests/cp38/test_zipimport_support.py rename to cpython-tests/3.8/test_zipimport_support.py diff --git a/cpython-tests/cp38/zip_cp437_header.zip b/cpython-tests/3.8/zip_cp437_header.zip similarity index 100% rename from cpython-tests/cp38/zip_cp437_header.zip rename to cpython-tests/3.8/zip_cp437_header.zip diff --git a/cpython-tests/cp38/zipdir.zip b/cpython-tests/3.8/zipdir.zip similarity index 100% rename from cpython-tests/cp38/zipdir.zip rename to cpython-tests/3.8/zipdir.zip diff --git a/cpython-tests/cp38/ziptestdata/README.md b/cpython-tests/3.8/ziptestdata/README.md similarity index 100% rename from cpython-tests/cp38/ziptestdata/README.md rename to cpython-tests/3.8/ziptestdata/README.md diff --git a/cpython-tests/cp38/ziptestdata/exe_with_z64 b/cpython-tests/3.8/ziptestdata/exe_with_z64 similarity index 100% rename from cpython-tests/cp38/ziptestdata/exe_with_z64 rename to cpython-tests/3.8/ziptestdata/exe_with_z64 diff --git a/cpython-tests/cp38/ziptestdata/exe_with_zip b/cpython-tests/3.8/ziptestdata/exe_with_zip similarity index 100% rename from cpython-tests/cp38/ziptestdata/exe_with_zip rename to cpython-tests/3.8/ziptestdata/exe_with_zip diff --git a/cpython-tests/cp38/ziptestdata/header.sh b/cpython-tests/3.8/ziptestdata/header.sh similarity index 100% rename from cpython-tests/cp38/ziptestdata/header.sh rename to cpython-tests/3.8/ziptestdata/header.sh diff --git a/cpython-tests/cp38/ziptestdata/testdata_module_inside_zip.py b/cpython-tests/3.8/ziptestdata/testdata_module_inside_zip.py similarity index 100% rename from cpython-tests/cp38/ziptestdata/testdata_module_inside_zip.py rename to cpython-tests/3.8/ziptestdata/testdata_module_inside_zip.py diff --git a/cpython-tests/cp39/test_zipfile.py b/cpython-tests/3.9/test_zipfile.py similarity index 100% rename from cpython-tests/cp39/test_zipfile.py rename to cpython-tests/3.9/test_zipfile.py diff --git a/cpython-tests/cp39/test_zipfile64.py b/cpython-tests/3.9/test_zipfile64.py similarity index 100% rename from cpython-tests/cp39/test_zipfile64.py rename to cpython-tests/3.9/test_zipfile64.py diff --git a/cpython-tests/cp39/test_zipimport.py b/cpython-tests/3.9/test_zipimport.py similarity index 100% rename from cpython-tests/cp39/test_zipimport.py rename to cpython-tests/3.9/test_zipimport.py diff --git a/cpython-tests/cp39/test_zipimport_support.py b/cpython-tests/3.9/test_zipimport_support.py similarity index 100% rename from cpython-tests/cp39/test_zipimport_support.py rename to cpython-tests/3.9/test_zipimport_support.py diff --git a/cpython-tests/cp39/zip_cp437_header.zip b/cpython-tests/3.9/zip_cp437_header.zip similarity index 100% rename from cpython-tests/cp39/zip_cp437_header.zip rename to cpython-tests/3.9/zip_cp437_header.zip diff --git a/cpython-tests/cp39/zipdir.zip b/cpython-tests/3.9/zipdir.zip similarity index 100% rename from cpython-tests/cp39/zipdir.zip rename to cpython-tests/3.9/zipdir.zip diff --git a/cpython-tests/cp39/ziptestdata/README.md b/cpython-tests/3.9/ziptestdata/README.md similarity index 100% rename from cpython-tests/cp39/ziptestdata/README.md rename to cpython-tests/3.9/ziptestdata/README.md diff --git a/cpython-tests/cp39/ziptestdata/exe_with_z64 b/cpython-tests/3.9/ziptestdata/exe_with_z64 similarity index 100% rename from cpython-tests/cp39/ziptestdata/exe_with_z64 rename to cpython-tests/3.9/ziptestdata/exe_with_z64 diff --git a/cpython-tests/cp39/ziptestdata/exe_with_zip b/cpython-tests/3.9/ziptestdata/exe_with_zip similarity index 100% rename from cpython-tests/cp39/ziptestdata/exe_with_zip rename to cpython-tests/3.9/ziptestdata/exe_with_zip diff --git a/cpython-tests/cp39/ziptestdata/header.sh b/cpython-tests/3.9/ziptestdata/header.sh similarity index 100% rename from cpython-tests/cp39/ziptestdata/header.sh rename to cpython-tests/3.9/ziptestdata/header.sh diff --git a/cpython-tests/cp39/ziptestdata/testdata_module_inside_zip.py b/cpython-tests/3.9/ziptestdata/testdata_module_inside_zip.py similarity index 100% rename from cpython-tests/cp39/ziptestdata/testdata_module_inside_zip.py rename to cpython-tests/3.9/ziptestdata/testdata_module_inside_zip.py diff --git a/cpython-tests/cp311/test_zipimport_support.py b/cpython-tests/cp311/test_zipimport_support.py deleted file mode 100644 index 7bf50a3..0000000 --- a/cpython-tests/cp311/test_zipimport_support.py +++ /dev/null @@ -1,243 +0,0 @@ -# This test module covers support in various parts of the standard library -# for working with modules located inside zipfiles -# The tests are centralised in this fashion to make it easy to drop them -# if a platform doesn't support zipimport -import test.support -import os -import os.path -import sys -import textwrap -import zipfile -import zipimport -import doctest -import inspect -import linecache -import unittest -from test.support import os_helper -from test.support.script_helper import (spawn_python, kill_python, assert_python_ok, - make_script, make_zip_script) - -verbose = test.support.verbose - -# Library modules covered by this test set -# pdb (Issue 4201) -# inspect (Issue 4223) -# doctest (Issue 4197) - -# Other test modules with zipimport related tests -# test_zipimport (of course!) -# test_cmd_line_script (covers the zipimport support in runpy) - -# Retrieve some helpers from other test cases -from test import (test_doctest, sample_doctest, sample_doctest_no_doctests, - sample_doctest_no_docstrings) - - -def _run_object_doctest(obj, module): - finder = doctest.DocTestFinder(verbose=verbose, recurse=False) - runner = doctest.DocTestRunner(verbose=verbose) - # Use the object's fully qualified name if it has one - # Otherwise, use the module's name - try: - name = "%s.%s" % (obj.__module__, obj.__qualname__) - except AttributeError: - name = module.__name__ - for example in finder.find(obj, name, module): - runner.run(example) - f, t = runner.failures, runner.tries - if f: - raise test.support.TestFailed("%d of %d doctests failed" % (f, t)) - if verbose: - print ('doctest (%s) ... %d tests with zero failures' % (module.__name__, t)) - return f, t - - - -class ZipSupportTests(unittest.TestCase): - # This used to use the ImportHooksBaseTestCase to restore - # the state of the import related information - # in the sys module after each test. However, that restores - # *too much* information and breaks for the invocation - # of test_doctest. So we do our own thing and leave - # sys.modules alone. - # We also clear the linecache and zipimport cache - # just to avoid any bogus errors due to name reuse in the tests - def setUp(self): - linecache.clearcache() - zipimport._zip_directory_cache.clear() - self.path = sys.path[:] - self.meta_path = sys.meta_path[:] - self.path_hooks = sys.path_hooks[:] - sys.path_importer_cache.clear() - - def tearDown(self): - sys.path[:] = self.path - sys.meta_path[:] = self.meta_path - sys.path_hooks[:] = self.path_hooks - sys.path_importer_cache.clear() - - def test_inspect_getsource_issue4223(self): - test_src = "def foo(): pass\n" - with os_helper.temp_dir() as d: - init_name = make_script(d, '__init__', test_src) - name_in_zip = os.path.join('zip_pkg', - os.path.basename(init_name)) - zip_name, run_name = make_zip_script(d, 'test_zip', - init_name, name_in_zip) - os.remove(init_name) - sys.path.insert(0, zip_name) - import zip_pkg - try: - self.assertEqual(inspect.getsource(zip_pkg.foo), test_src) - finally: - del sys.modules["zip_pkg"] - - def test_doctest_issue4197(self): - # To avoid having to keep two copies of the doctest module's - # unit tests in sync, this test works by taking the source of - # test_doctest itself, rewriting it a bit to cope with a new - # location, and then throwing it in a zip file to make sure - # everything still works correctly - test_src = inspect.getsource(test_doctest) - test_src = test_src.replace( - "from test import test_doctest", - "import test_zipped_doctest as test_doctest") - test_src = test_src.replace("test.test_doctest", - "test_zipped_doctest") - test_src = test_src.replace("test.sample_doctest", - "sample_zipped_doctest") - # The sample doctest files rewritten to include in the zipped version. - sample_sources = {} - for mod in [sample_doctest, sample_doctest_no_doctests, - sample_doctest_no_docstrings]: - src = inspect.getsource(mod) - src = src.replace("test.test_doctest", "test_zipped_doctest") - # Rewrite the module name so that, for example, - # "test.sample_doctest" becomes "sample_zipped_doctest". - mod_name = mod.__name__.split(".")[-1] - mod_name = mod_name.replace("sample_", "sample_zipped_") - sample_sources[mod_name] = src - - with os_helper.temp_dir() as d: - script_name = make_script(d, 'test_zipped_doctest', - test_src) - zip_name, run_name = make_zip_script(d, 'test_zip', - script_name) - with zipfile.ZipFile(zip_name, 'a') as z: - for mod_name, src in sample_sources.items(): - z.writestr(mod_name + ".py", src) - if verbose: - with zipfile.ZipFile(zip_name, 'r') as zip_file: - print ('Contents of %r:' % zip_name) - zip_file.printdir() - os.remove(script_name) - sys.path.insert(0, zip_name) - import test_zipped_doctest - try: - # Some of the doc tests depend on the colocated text files - # which aren't available to the zipped version (the doctest - # module currently requires real filenames for non-embedded - # tests). So we're forced to be selective about which tests - # to run. - # doctest could really use some APIs which take a text - # string or a file object instead of a filename... - known_good_tests = [ - test_zipped_doctest.SampleClass, - test_zipped_doctest.SampleClass.NestedClass, - test_zipped_doctest.SampleClass.NestedClass.__init__, - test_zipped_doctest.SampleClass.__init__, - test_zipped_doctest.SampleClass.a_classmethod, - test_zipped_doctest.SampleClass.a_property, - test_zipped_doctest.SampleClass.a_staticmethod, - test_zipped_doctest.SampleClass.double, - test_zipped_doctest.SampleClass.get, - test_zipped_doctest.SampleNewStyleClass, - test_zipped_doctest.SampleNewStyleClass.__init__, - test_zipped_doctest.SampleNewStyleClass.double, - test_zipped_doctest.SampleNewStyleClass.get, - test_zipped_doctest.sample_func, - test_zipped_doctest.test_DocTest, - test_zipped_doctest.test_DocTestParser, - test_zipped_doctest.test_DocTestRunner.basics, - test_zipped_doctest.test_DocTestRunner.exceptions, - test_zipped_doctest.test_DocTestRunner.option_directives, - test_zipped_doctest.test_DocTestRunner.optionflags, - test_zipped_doctest.test_DocTestRunner.verbose_flag, - test_zipped_doctest.test_Example, - test_zipped_doctest.test_debug, - test_zipped_doctest.test_testsource, - test_zipped_doctest.test_trailing_space_in_test, - test_zipped_doctest.test_DocTestSuite, - test_zipped_doctest.test_DocTestFinder, - ] - # These tests are the ones which need access - # to the data files, so we don't run them - fail_due_to_missing_data_files = [ - test_zipped_doctest.test_DocFileSuite, - test_zipped_doctest.test_testfile, - test_zipped_doctest.test_unittest_reportflags, - ] - - for obj in known_good_tests: - _run_object_doctest(obj, test_zipped_doctest) - finally: - del sys.modules["test_zipped_doctest"] - - def test_doctest_main_issue4197(self): - test_src = textwrap.dedent("""\ - class Test: - ">>> 'line 2'" - pass - - import doctest - doctest.testmod() - """) - pattern = 'File "%s", line 2, in %s' - with os_helper.temp_dir() as d: - script_name = make_script(d, 'script', test_src) - rc, out, err = assert_python_ok(script_name) - expected = pattern % (script_name, "__main__.Test") - if verbose: - print ("Expected line", expected) - print ("Got stdout:") - print (ascii(out)) - self.assertIn(expected.encode('utf-8'), out) - zip_name, run_name = make_zip_script(d, "test_zip", - script_name, '__main__.py') - rc, out, err = assert_python_ok(zip_name) - expected = pattern % (run_name, "__main__.Test") - if verbose: - print ("Expected line", expected) - print ("Got stdout:") - print (ascii(out)) - self.assertIn(expected.encode('utf-8'), out) - - def test_pdb_issue4201(self): - test_src = textwrap.dedent("""\ - def f(): - pass - - import pdb - pdb.Pdb(nosigint=True).runcall(f) - """) - with os_helper.temp_dir() as d: - script_name = make_script(d, 'script', test_src) - p = spawn_python(script_name) - p.stdin.write(b'l\n') - data = kill_python(p) - # bdb/pdb applies normcase to its filename before displaying - self.assertIn(os.path.normcase(script_name.encode('utf-8')), data) - zip_name, run_name = make_zip_script(d, "test_zip", - script_name, '__main__.py') - p = spawn_python(zip_name) - p.stdin.write(b'l\n') - data = kill_python(p) - # bdb/pdb applies normcase to its filename before displaying - self.assertIn(os.path.normcase(run_name.encode('utf-8')), data) - - -def tearDownModule(): - test.support.reap_children() - -if __name__ == '__main__': - unittest.main() From 8c4524791b04b3b0c5b93bc18bd3d90553872326 Mon Sep 17 00:00:00 2001 From: Ioannis Tziakos Date: Sat, 5 Oct 2024 11:29:49 +0100 Subject: [PATCH 08/41] Run integration tests on github test workflow --- .github/workflows/test.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 3ec7934..9261ad1 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -10,7 +10,7 @@ jobs: tests: strategy: matrix: - python-version: [3.8, 3.11, 3.12] + python-version: ['3.8', '3.9', '3.10', '3.11', '3.12'] os: [ubuntu-latest, macos-latest, windows-latest] runs-on: ${{ matrix.os }} steps: @@ -25,6 +25,9 @@ jobs: run: python -m pip install -r "dev_requirements.txt" - name: Test with haas run: python -m haas zipfile2 + - name: Integration tests + run: python -m haas cpython/${{ matrix.python-version }} + code-lint: runs-on: ubuntu-latest steps: From b20c95fba41feb1493aff6ef55cd0de055104665 Mon Sep 17 00:00:00 2001 From: Ioannis Tziakos Date: Sat, 5 Oct 2024 11:31:40 +0100 Subject: [PATCH 09/41] minor cleanup --- zipfile2/_zipfile.py | 59 +++++++++++++++++++++++++++++--------------- 1 file changed, 39 insertions(+), 20 deletions(-) diff --git a/zipfile2/_zipfile.py b/zipfile2/_zipfile.py index cf2deb0..86b646d 100644 --- a/zipfile2/_zipfile.py +++ b/zipfile2/_zipfile.py @@ -31,24 +31,34 @@ class ZipFile(zipfile.ZipFile): This also support context management on 2.6. """ - def __init__(self, file, mode='r', compression=zipfile.ZIP_STORED, - allowZip64=True, low_level=False): - """ Open the ZIP file. + def __init__( + self, file, mode='r', compression=zipfile.ZIP_STORED, + allowZip64=True, compresslevel=None, *, strict_timestamps=True, + metadata_encoding=None, low_level=False): + """Open the ZIP file. Parameters ---------- file: str Filename mode: str - 'r' for read, 'w' for write, 'a' for append + The mode can be either read 'r', write 'w', exclusive create 'x', + or append 'a'. compression: int - Which compression method to use. + ZIP_STORED (no compression), ZIP_DEFLATED (requires zlib), + ZIP_BZIP2 (requires bz2) or ZIP_LZMA (requires lzma). + allowZip64: bool + if True ZipFile will create files with ZIP64 extensions + when needed, otherwise it will raise an exception when + this would be necessary. low_level: bool If False, will raise an error when adding an already existing archive. - """ - super(ZipFile, self).__init__(file, mode, compression, allowZip64) + """ + super(ZipFile, self).__init__( + file, mode, compression, allowZip64, compresslevel, + strict_timestamps=strict_timestamps, metadata_encoding=metadata_encoding) self.low_level = low_level # Set of filenames currently in file @@ -83,18 +93,23 @@ def add_tree(self, directory, include_top=False): arcname = os.path.join(base, os.path.relpath(entry, directory)) self.write(entry, arcname) - def extract(self, member, path=None, pwd=None, - preserve_permissions=PERMS_PRESERVE_NONE): + def extract( + self, member, path=None, pwd=None, + preserve_permissions=PERMS_PRESERVE_NONE): + if not isinstance(member, zipfile.ZipInfo): member = self.getinfo(member) if path is None: path = os.getcwd() + else: + path = os.fspath(path) return self._extract_member(member, path, pwd, preserve_permissions) - def extractall(self, path=None, members=None, pwd=None, - preserve_permissions=PERMS_PRESERVE_NONE): + def extractall( + self, path=None, members=None, pwd=None, + preserve_permissions=PERMS_PRESERVE_NONE): """ Extract all members from the archive to the current working directory. @@ -129,13 +144,13 @@ def extract_to(self, member, destination, path=None, pwd=None, return self._extract_member_to(member, destination, path, pwd, preserve_permissions) - def write(self, filename, arcname=None, compress_type=None): + def write( + self, filename, arcname=None, + compress_type=None, compresslevel=None): if arcname is None: arcname = filename st = os.lstat(filename) - arcname = self._normalize_arcname(arcname) - if stat.S_ISDIR(st.st_mode): arcname += '/' @@ -149,24 +164,28 @@ def write(self, filename, arcname=None, compress_type=None): zip_info.external_attr = ZIP_SOFTLINK_ATTRIBUTE_MAGIC self.writestr(zip_info, os.readlink(filename)) else: - super(ZipFile, self).write(filename, arcname, compress_type) + super(ZipFile, self).write( + filename, arcname, compress_type, compresslevel) self._filenames_set.add(arcname) - def writestr(self, zinfo_or_arcname, bytes, compress_type=None): + def writestr( + self, zinfo_or_arcname, data, + compress_type=None, compresslevel=None): if not isinstance(zinfo_or_arcname, zipfile.ZipInfo): arcname = self._normalize_arcname(zinfo_or_arcname) else: arcname = zinfo_or_arcname.filename self._ensure_uniqueness(arcname) - self._filenames_set.add(arcname) - super(ZipFile, self).writestr(zinfo_or_arcname, bytes, compress_type) + super(ZipFile, self).writestr( + zinfo_or_arcname, data, compress_type, compresslevel) # Overriden so that ZipFile.extract* support softlink def _extract_member(self, member, targetpath, pwd, preserve_permissions): - return self._extract_member_to(member, member.filename, - targetpath, pwd, preserve_permissions) + return self._extract_member_to( + member, member.filename, + targetpath, pwd, preserve_permissions) def _extract_symlink(self, member, link_name, pwd=None): source = self.read(member).decode("utf8") From 5014b70316821ca4ff866d8576242076e400755e Mon Sep 17 00:00:00 2001 From: Ioannis Tziakos Date: Sat, 5 Oct 2024 11:34:43 +0100 Subject: [PATCH 10/41] fix flake8 errors --- zipfile2/_zipfile.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/zipfile2/_zipfile.py b/zipfile2/_zipfile.py index 86b646d..c4b521c 100644 --- a/zipfile2/_zipfile.py +++ b/zipfile2/_zipfile.py @@ -58,7 +58,8 @@ def __init__( """ super(ZipFile, self).__init__( file, mode, compression, allowZip64, compresslevel, - strict_timestamps=strict_timestamps, metadata_encoding=metadata_encoding) + strict_timestamps=strict_timestamps, + metadata_encoding=metadata_encoding) self.low_level = low_level # Set of filenames currently in file From d0eb7b20519560f39aba7bfe7cba45ffdafacf68 Mon Sep 17 00:00:00 2001 From: Ioannis Tziakos Date: Sat, 5 Oct 2024 11:36:31 +0100 Subject: [PATCH 11/41] Fix folder name --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 9261ad1..ccbade3 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -26,7 +26,7 @@ jobs: - name: Test with haas run: python -m haas zipfile2 - name: Integration tests - run: python -m haas cpython/${{ matrix.python-version }} + run: python -m haas cpython-tests/${{ matrix.python-version }} code-lint: runs-on: ubuntu-latest From 97f502725e9221d02f8129ced519abcead7da34b Mon Sep 17 00:00:00 2001 From: Ioannis Tziakos Date: Sat, 5 Oct 2024 11:48:30 +0100 Subject: [PATCH 12/41] Fix compatibility with Python < 3.11 --- zipfile2/_zipfile.py | 19 +++++++++++++++---- zipfile2/common.py | 4 ++++ 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/zipfile2/_zipfile.py b/zipfile2/_zipfile.py index c4b521c..ef72e12 100644 --- a/zipfile2/_zipfile.py +++ b/zipfile2/_zipfile.py @@ -5,6 +5,9 @@ import string import time import zipfile +import warnings + +from .common import PY311, metadata_encoding_warning ZIP_SOFTLINK_ATTRIBUTE_MAGIC = 0xA1ED0000 @@ -56,10 +59,18 @@ def __init__( archive. """ - super(ZipFile, self).__init__( - file, mode, compression, allowZip64, compresslevel, - strict_timestamps=strict_timestamps, - metadata_encoding=metadata_encoding) + if PY311: + super(ZipFile, self).__init__( + file, mode, compression, allowZip64, compresslevel, + strict_timestamps=strict_timestamps, + metadata_encoding=metadata_encoding) + else: + if metadata_encoding is not None: + warnings.warn(metadata_encoding_warning) + super(ZipFile, self).__init__( + file, mode, compression, allowZip64, compresslevel, + strict_timestamps=strict_timestamps) + self.low_level = low_level # Set of filenames currently in file diff --git a/zipfile2/common.py b/zipfile2/common.py index 47f0694..df6c9fe 100644 --- a/zipfile2/common.py +++ b/zipfile2/common.py @@ -4,7 +4,11 @@ PYTHON_VERSION = sys.version_info[:2] PY312 = PYTHON_VERSION >= (3, 12) +PY311 = PYTHON_VERSION >= (3, 11) class TooManyFiles(zipfile.BadZipfile): pass + + +metadata_encoding_warning = 'Python < 3.11 does not support the metadata_encoding' # noqa From 8f433bebce16ce98e655b08216b973de6cc0bfd5 Mon Sep 17 00:00:00 2001 From: Ioannis Tziakos Date: Sat, 5 Oct 2024 11:57:22 +0100 Subject: [PATCH 13/41] Adapt python tests to use and test zipfile2.ZipFile --- cpython-tests/3.10/test_zipfile.py | 428 ++++++++++--------- cpython-tests/3.10/test_zipfile64.py | 18 +- cpython-tests/3.10/test_zipimport.py | 4 +- cpython-tests/3.10/test_zipimport_support.py | 243 ----------- cpython-tests/3.11/test_zipfile64.py | 18 +- cpython-tests/3.11/test_zipimport.py | 4 +- cpython-tests/3.12/test_zipfile64.py | 18 +- cpython-tests/3.12/test_zipimport.py | 5 +- cpython-tests/3.12/test_zipimport_support.py | 244 ----------- cpython-tests/3.8/test_zipfile.py | 408 +++++++++--------- cpython-tests/3.8/test_zipfile64.py | 18 +- cpython-tests/3.8/test_zipimport.py | 5 +- cpython-tests/3.8/test_zipimport_support.py | 242 ----------- cpython-tests/3.9/test_zipfile.py | 414 +++++++++--------- cpython-tests/3.9/test_zipfile64.py | 19 +- cpython-tests/3.9/test_zipimport.py | 5 +- cpython-tests/3.9/test_zipimport_support.py | 242 ----------- 17 files changed, 697 insertions(+), 1638 deletions(-) delete mode 100644 cpython-tests/3.10/test_zipimport_support.py delete mode 100644 cpython-tests/3.12/test_zipimport_support.py delete mode 100644 cpython-tests/3.8/test_zipimport_support.py delete mode 100644 cpython-tests/3.9/test_zipimport_support.py diff --git a/cpython-tests/3.10/test_zipfile.py b/cpython-tests/3.10/test_zipfile.py index b4f71a8..7ac8ffa 100644 --- a/cpython-tests/3.10/test_zipfile.py +++ b/cpython-tests/3.10/test_zipfile.py @@ -26,6 +26,8 @@ requires_lzma, captured_stdout) from test.support.os_helper import TESTFN, unlink, rmtree, temp_dir, temp_cwd +import zipfile2 + TESTFN2 = TESTFN + "2" TESTFNDIR = TESTFN + "d" @@ -62,7 +64,7 @@ def setUp(self): def make_test_archive(self, f, compression, compresslevel=None): kwargs = {'compression': compression, 'compresslevel': compresslevel} # Create the ZIP archive - with zipfile.ZipFile(f, "w", **kwargs) as zipfp: + with zipfile2.ZipFile(f, "w", **kwargs) as zipfp: zipfp.write(TESTFN, "another.name") zipfp.write(TESTFN, TESTFN) zipfp.writestr("strfile", self.data) @@ -74,7 +76,7 @@ def zip_test(self, f, compression, compresslevel=None): self.make_test_archive(f, compression, compresslevel) # Read the ZIP archive - with zipfile.ZipFile(f, "r", compression) as zipfp: + with zipfile2.ZipFile(f, "r", compression) as zipfp: self.assertEqual(zipfp.read(TESTFN), self.data) self.assertEqual(zipfp.read("another.name"), self.data) self.assertEqual(zipfp.read("strfile"), self.data) @@ -132,7 +134,7 @@ def zip_open_test(self, f, compression): self.make_test_archive(f, compression) # Read the ZIP archive - with zipfile.ZipFile(f, "r", compression) as zipfp: + with zipfile2.ZipFile(f, "r", compression) as zipfp: zipdata1 = [] with zipfp.open(TESTFN) as zipopen1: while True: @@ -159,14 +161,14 @@ def test_open(self): def test_open_with_pathlike(self): path = pathlib.Path(TESTFN2) self.zip_open_test(path, self.compression) - with zipfile.ZipFile(path, "r", self.compression) as zipfp: + with zipfile2.ZipFile(path, "r", self.compression) as zipfp: self.assertIsInstance(zipfp.filename, str) def zip_random_open_test(self, f, compression): self.make_test_archive(f, compression) # Read the ZIP archive - with zipfile.ZipFile(f, "r", compression) as zipfp: + with zipfile2.ZipFile(f, "r", compression) as zipfp: zipdata1 = [] with zipfp.open(TESTFN) as zipopen1: while True: @@ -185,7 +187,7 @@ def zip_read1_test(self, f, compression): self.make_test_archive(f, compression) # Read the ZIP archive - with zipfile.ZipFile(f, "r") as zipfp, \ + with zipfile2.ZipFile(f, "r") as zipfp, \ zipfp.open(TESTFN) as zipopen: zipdata = [] while True: @@ -204,7 +206,7 @@ def zip_read1_10_test(self, f, compression): self.make_test_archive(f, compression) # Read the ZIP archive - with zipfile.ZipFile(f, "r") as zipfp, \ + with zipfile2.ZipFile(f, "r") as zipfp, \ zipfp.open(TESTFN) as zipopen: zipdata = [] while True: @@ -224,7 +226,7 @@ def zip_readline_read_test(self, f, compression): self.make_test_archive(f, compression) # Read the ZIP archive - with zipfile.ZipFile(f, "r") as zipfp, \ + with zipfile2.ZipFile(f, "r") as zipfp, \ zipfp.open(TESTFN) as zipopen: data = b'' while True: @@ -249,7 +251,7 @@ def zip_readline_test(self, f, compression): self.make_test_archive(f, compression) # Read the ZIP archive - with zipfile.ZipFile(f, "r") as zipfp: + with zipfile2.ZipFile(f, "r") as zipfp: with zipfp.open(TESTFN) as zipopen: for line in self.line_gen: linedata = zipopen.readline() @@ -263,7 +265,7 @@ def zip_readlines_test(self, f, compression): self.make_test_archive(f, compression) # Read the ZIP archive - with zipfile.ZipFile(f, "r") as zipfp: + with zipfile2.ZipFile(f, "r") as zipfp: with zipfp.open(TESTFN) as zipopen: ziplines = zipopen.readlines() for line, zipline in zip(self.line_gen, ziplines): @@ -277,7 +279,7 @@ def zip_iterlines_test(self, f, compression): self.make_test_archive(f, compression) # Read the ZIP archive - with zipfile.ZipFile(f, "r") as zipfp: + with zipfile2.ZipFile(f, "r") as zipfp: with zipfp.open(TESTFN) as zipopen: for line, zipline in zip(self.line_gen, zipopen): self.assertEqual(zipline, line) @@ -289,23 +291,23 @@ def test_iterlines(self): def test_low_compression(self): """Check for cases where compressed data is larger than original.""" # Create the ZIP archive - with zipfile.ZipFile(TESTFN2, "w", self.compression) as zipfp: + with zipfile2.ZipFile(TESTFN2, "w", self.compression) as zipfp: zipfp.writestr("strfile", '12') # Get an open object for strfile - with zipfile.ZipFile(TESTFN2, "r", self.compression) as zipfp: + with zipfile2.ZipFile(TESTFN2, "r", self.compression) as zipfp: with zipfp.open("strfile") as openobj: self.assertEqual(openobj.read(1), b'1') self.assertEqual(openobj.read(1), b'2') def test_writestr_compression(self): - zipfp = zipfile.ZipFile(TESTFN2, "w") + zipfp = zipfile2.ZipFile(TESTFN2, "w") zipfp.writestr("b.txt", "hello world", compress_type=self.compression) info = zipfp.getinfo('b.txt') self.assertEqual(info.compress_type, self.compression) def test_writestr_compresslevel(self): - zipfp = zipfile.ZipFile(TESTFN2, "w", compresslevel=1) + zipfp = zipfile2.ZipFile(TESTFN2, "w", compresslevel=1) zipfp.writestr("a.txt", "hello world", compress_type=self.compression) zipfp.writestr("b.txt", "hello world", compress_type=self.compression, compresslevel=2) @@ -326,7 +328,7 @@ def test_read_return_size(self): for test_size in (1, 4095, 4096, 4097, 16384): file_size = test_size + 1 junk = randbytes(file_size) - with zipfile.ZipFile(io.BytesIO(), "w", self.compression) as zipf: + with zipfile2.ZipFile(io.BytesIO(), "w", self.compression) as zipf: zipf.writestr('foo', junk) with zipf.open('foo', 'r') as fp: buf = fp.read(test_size) @@ -334,20 +336,20 @@ def test_read_return_size(self): def test_truncated_zipfile(self): fp = io.BytesIO() - with zipfile.ZipFile(fp, mode='w') as zipf: + with zipfile2.ZipFile(fp, mode='w') as zipf: zipf.writestr('strfile', self.data, compress_type=self.compression) end_offset = fp.tell() zipfiledata = fp.getvalue() fp = io.BytesIO(zipfiledata) - with zipfile.ZipFile(fp) as zipf: + with zipfile2.ZipFile(fp) as zipf: with zipf.open('strfile') as zipopen: fp.truncate(end_offset - 20) with self.assertRaises(EOFError): zipopen.read() fp = io.BytesIO(zipfiledata) - with zipfile.ZipFile(fp) as zipf: + with zipfile2.ZipFile(fp) as zipf: with zipf.open('strfile') as zipopen: fp.truncate(end_offset - 20) with self.assertRaises(EOFError): @@ -355,7 +357,7 @@ def test_truncated_zipfile(self): pass fp = io.BytesIO(zipfiledata) - with zipfile.ZipFile(fp) as zipf: + with zipfile2.ZipFile(fp) as zipf: with zipf.open('strfile') as zipopen: fp.truncate(end_offset - 20) with self.assertRaises(EOFError): @@ -365,12 +367,12 @@ def test_truncated_zipfile(self): def test_repr(self): fname = 'file.name' for f in get_files(self): - with zipfile.ZipFile(f, 'w', self.compression) as zipfp: + with zipfile2.ZipFile(f, 'w', self.compression) as zipfp: zipfp.write(TESTFN, fname) r = repr(zipfp) self.assertIn("mode='w'", r) - with zipfile.ZipFile(f, 'r') as zipfp: + with zipfile2.ZipFile(f, 'r') as zipfp: r = repr(zipfp) if isinstance(f, str): self.assertIn('filename=%r' % f, r) @@ -400,7 +402,7 @@ def test_compresslevel_basic(self): def test_per_file_compresslevel(self): """Check that files within a Zip archive can have different compression levels.""" - with zipfile.ZipFile(TESTFN2, "w", compresslevel=1) as zipfp: + with zipfile2.ZipFile(TESTFN2, "w", compresslevel=1) as zipfp: zipfp.write(TESTFN, 'compress_1') zipfp.write(TESTFN, 'compress_9', compresslevel=9) one_info = zipfp.getinfo('compress_1') @@ -422,7 +424,7 @@ def write(self, data): while True: testfile = BrokenFile() count = None - with zipfile.ZipFile(testfile, 'w', self.compression) as zipfp: + with zipfile2.ZipFile(testfile, 'w', self.compression) as zipfp: with zipfp.open('file1', 'w') as f: f.write(b'data1') count = 0 @@ -435,11 +437,11 @@ def write(self, data): break finally: count = None - with zipfile.ZipFile(io.BytesIO(testfile.getvalue())) as zipfp: + with zipfile2.ZipFile(io.BytesIO(testfile.getvalue())) as zipfp: self.assertEqual(zipfp.namelist(), ['file1']) self.assertEqual(zipfp.read('file1'), b'data1') - with zipfile.ZipFile(io.BytesIO(testfile.getvalue())) as zipfp: + with zipfile2.ZipFile(io.BytesIO(testfile.getvalue())) as zipfp: self.assertEqual(zipfp.namelist(), ['file1', 'file2']) self.assertEqual(zipfp.read('file1'), b'data1') self.assertEqual(zipfp.read('file2'), b'data2') @@ -461,7 +463,7 @@ def zip_test_writestr_permissions(self, f, compression): # instance. self.make_test_archive(f, compression) - with zipfile.ZipFile(f, "r") as zipfp: + with zipfile2.ZipFile(f, "r") as zipfp: zinfo = zipfp.getinfo('strfile') self.assertEqual(zinfo.external_attr, 0o600 << 16) @@ -473,18 +475,18 @@ def test_writestr_permissions(self): self.zip_test_writestr_permissions(f, zipfile.ZIP_STORED) def test_absolute_arcnames(self): - with zipfile.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED) as zipfp: + with zipfile2.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED) as zipfp: zipfp.write(TESTFN, "/absolute") - with zipfile.ZipFile(TESTFN2, "r", zipfile.ZIP_STORED) as zipfp: + with zipfile2.ZipFile(TESTFN2, "r", zipfile.ZIP_STORED) as zipfp: self.assertEqual(zipfp.namelist(), ["absolute"]) def test_append_to_zip_file(self): """Test appending to an existing zipfile.""" - with zipfile.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED) as zipfp: + with zipfile2.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED) as zipfp: zipfp.write(TESTFN, TESTFN) - with zipfile.ZipFile(TESTFN2, "a", zipfile.ZIP_STORED) as zipfp: + with zipfile2.ZipFile(TESTFN2, "a", zipfile.ZIP_STORED) as zipfp: zipfp.writestr("strfile", self.data) self.assertEqual(zipfp.namelist(), [TESTFN, "strfile"]) @@ -496,24 +498,24 @@ def test_append_to_non_zip_file(self): with open(TESTFN2, 'wb') as f: f.write(data) - with zipfile.ZipFile(TESTFN2, "a", zipfile.ZIP_STORED) as zipfp: + with zipfile2.ZipFile(TESTFN2, "a", zipfile.ZIP_STORED) as zipfp: zipfp.write(TESTFN, TESTFN) with open(TESTFN2, 'rb') as f: f.seek(len(data)) - with zipfile.ZipFile(f, "r") as zipfp: + with zipfile2.ZipFile(f, "r") as zipfp: self.assertEqual(zipfp.namelist(), [TESTFN]) self.assertEqual(zipfp.read(TESTFN), self.data) with open(TESTFN2, 'rb') as f: self.assertEqual(f.read(len(data)), data) zipfiledata = f.read() - with io.BytesIO(zipfiledata) as bio, zipfile.ZipFile(bio) as zipfp: + with io.BytesIO(zipfiledata) as bio, zipfile2.ZipFile(bio) as zipfp: self.assertEqual(zipfp.namelist(), [TESTFN]) self.assertEqual(zipfp.read(TESTFN), self.data) def test_read_concatenated_zip_file(self): with io.BytesIO() as bio: - with zipfile.ZipFile(bio, 'w', zipfile.ZIP_STORED) as zipfp: + with zipfile2.ZipFile(bio, 'w', zipfile.ZIP_STORED) as zipfp: zipfp.write(TESTFN, TESTFN) zipfiledata = bio.getvalue() data = b'I am not a ZipFile!'*10 @@ -521,13 +523,13 @@ def test_read_concatenated_zip_file(self): f.write(data) f.write(zipfiledata) - with zipfile.ZipFile(TESTFN2) as zipfp: + with zipfile2.ZipFile(TESTFN2) as zipfp: self.assertEqual(zipfp.namelist(), [TESTFN]) self.assertEqual(zipfp.read(TESTFN), self.data) def test_append_to_concatenated_zip_file(self): with io.BytesIO() as bio: - with zipfile.ZipFile(bio, 'w', zipfile.ZIP_STORED) as zipfp: + with zipfile2.ZipFile(bio, 'w', zipfile.ZIP_STORED) as zipfp: zipfp.write(TESTFN, TESTFN) zipfiledata = bio.getvalue() data = b'I am not a ZipFile!'*1000000 @@ -535,50 +537,50 @@ def test_append_to_concatenated_zip_file(self): f.write(data) f.write(zipfiledata) - with zipfile.ZipFile(TESTFN2, 'a') as zipfp: + with zipfile2.ZipFile(TESTFN2, 'a') as zipfp: self.assertEqual(zipfp.namelist(), [TESTFN]) zipfp.writestr('strfile', self.data) with open(TESTFN2, 'rb') as f: self.assertEqual(f.read(len(data)), data) zipfiledata = f.read() - with io.BytesIO(zipfiledata) as bio, zipfile.ZipFile(bio) as zipfp: + with io.BytesIO(zipfiledata) as bio, zipfile2.ZipFile(bio) as zipfp: self.assertEqual(zipfp.namelist(), [TESTFN, 'strfile']) self.assertEqual(zipfp.read(TESTFN), self.data) self.assertEqual(zipfp.read('strfile'), self.data) def test_ignores_newline_at_end(self): - with zipfile.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED) as zipfp: + with zipfile2.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED) as zipfp: zipfp.write(TESTFN, TESTFN) with open(TESTFN2, 'a', encoding='utf-8') as f: f.write("\r\n\00\00\00") - with zipfile.ZipFile(TESTFN2, "r") as zipfp: - self.assertIsInstance(zipfp, zipfile.ZipFile) + with zipfile2.ZipFile(TESTFN2, "r") as zipfp: + self.assertIsInstance(zipfp, zipfile2.ZipFile) def test_ignores_stuff_appended_past_comments(self): - with zipfile.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED) as zipfp: + with zipfile2.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED) as zipfp: zipfp.comment = b"this is a comment" zipfp.write(TESTFN, TESTFN) with open(TESTFN2, 'a', encoding='utf-8') as f: f.write("abcdef\r\n") - with zipfile.ZipFile(TESTFN2, "r") as zipfp: - self.assertIsInstance(zipfp, zipfile.ZipFile) + with zipfile2.ZipFile(TESTFN2, "r") as zipfp: + self.assertIsInstance(zipfp, zipfile2.ZipFile) self.assertEqual(zipfp.comment, b"this is a comment") def test_write_default_name(self): """Check that calling ZipFile.write without arcname specified produces the expected result.""" - with zipfile.ZipFile(TESTFN2, "w") as zipfp: + with zipfile2.ZipFile(TESTFN2, "w") as zipfp: zipfp.write(TESTFN) with open(TESTFN, "rb") as f: self.assertEqual(zipfp.read(TESTFN), f.read()) def test_io_on_closed_zipextfile(self): fname = "somefile.txt" - with zipfile.ZipFile(TESTFN2, mode="w") as zipfp: + with zipfile2.ZipFile(TESTFN2, mode="w") as zipfp: zipfp.writestr(fname, "bogus") - with zipfile.ZipFile(TESTFN2, mode="r") as zipfp: + with zipfile2.ZipFile(TESTFN2, mode="r") as zipfp: with zipfp.open(fname) as fid: fid.close() self.assertRaises(ValueError, fid.read) @@ -590,23 +592,23 @@ def test_io_on_closed_zipextfile(self): def test_write_to_readonly(self): """Check that trying to call write() on a readonly ZipFile object raises a ValueError.""" - with zipfile.ZipFile(TESTFN2, mode="w") as zipfp: + with zipfile2.ZipFile(TESTFN2, mode="w") as zipfp: zipfp.writestr("somefile.txt", "bogus") - with zipfile.ZipFile(TESTFN2, mode="r") as zipfp: + with zipfile2.ZipFile(TESTFN2, mode="r") as zipfp: self.assertRaises(ValueError, zipfp.write, TESTFN) - with zipfile.ZipFile(TESTFN2, mode="r") as zipfp: + with zipfile2.ZipFile(TESTFN2, mode="r") as zipfp: with self.assertRaises(ValueError): zipfp.open(TESTFN, mode='w') def test_add_file_before_1980(self): # Set atime and mtime to 1970-01-01 os.utime(TESTFN, (0, 0)) - with zipfile.ZipFile(TESTFN2, "w") as zipfp: + with zipfile2.ZipFile(TESTFN2, "w") as zipfp: self.assertRaises(ValueError, zipfp.write, TESTFN) - with zipfile.ZipFile(TESTFN2, "w", strict_timestamps=False) as zipfp: + with zipfile2.ZipFile(TESTFN2, "w", strict_timestamps=False) as zipfp: zipfp.write(TESTFN) zinfo = zipfp.getinfo(TESTFN) self.assertEqual(zinfo.date_time, (1980, 1, 1, 0, 0, 0)) @@ -635,10 +637,10 @@ def test_add_file_after_2107(self): # https://bugs.python.org/issue39460#msg360952 self.skipTest(f"Linux VFS/XFS kernel bug detected: {mtime_ns=}") - with zipfile.ZipFile(TESTFN2, "w") as zipfp: + with zipfile2.ZipFile(TESTFN2, "w") as zipfp: self.assertRaises(struct.error, zipfp.write, TESTFN) - with zipfile.ZipFile(TESTFN2, "w", strict_timestamps=False) as zipfp: + with zipfile2.ZipFile(TESTFN2, "w", strict_timestamps=False) as zipfp: zipfp.write(TESTFN) zinfo = zipfp.getinfo(TESTFN) self.assertEqual(zinfo.date_time, (2107, 12, 31, 23, 59, 59)) @@ -652,7 +654,7 @@ class DeflateTestsWithSourceFile(AbstractTestsWithSourceFile, def test_per_file_compression(self): """Check that files within a Zip archive can have different compression options.""" - with zipfile.ZipFile(TESTFN2, "w") as zipfp: + with zipfile2.ZipFile(TESTFN2, "w") as zipfp: zipfp.write(TESTFN, 'storeme', zipfile.ZIP_STORED) zipfp.write(TESTFN, 'deflateme', zipfile.ZIP_DEFLATED) sinfo = zipfp.getinfo('storeme') @@ -693,13 +695,13 @@ def setUp(self): def zip_test(self, f, compression): # Create the ZIP archive - with zipfile.ZipFile(f, "w", compression, allowZip64=True) as zipfp: + with zipfile2.ZipFile(f, "w", compression, allowZip64=True) as zipfp: zipfp.write(TESTFN, "another.name") zipfp.write(TESTFN, TESTFN) zipfp.writestr("strfile", self.data) # Read the ZIP archive - with zipfile.ZipFile(f, "r", compression) as zipfp: + with zipfile2.ZipFile(f, "r", compression) as zipfp: self.assertEqual(zipfp.read(TESTFN), self.data) self.assertEqual(zipfp.read("another.name"), self.data) self.assertEqual(zipfp.read("strfile"), self.data) @@ -755,7 +757,7 @@ def test_basic(self): def test_too_many_files(self): # This test checks that more than 64k files can be added to an archive, # and that the resulting archive can be read properly by ZipFile - zipf = zipfile.ZipFile(TESTFN, "w", self.compression, + zipf = zipfile2.ZipFile(TESTFN, "w", self.compression, allowZip64=True) zipf.debug = 100 numfiles = 15 @@ -764,7 +766,7 @@ def test_too_many_files(self): self.assertEqual(len(zipf.namelist()), numfiles) zipf.close() - zipf2 = zipfile.ZipFile(TESTFN, "r", self.compression) + zipf2 = zipfile2.ZipFile(TESTFN, "r", self.compression) self.assertEqual(len(zipf2.namelist()), numfiles) for i in range(numfiles): content = zipf2.read("foo%08d" % i).decode('ascii') @@ -772,7 +774,7 @@ def test_too_many_files(self): zipf2.close() def test_too_many_files_append(self): - zipf = zipfile.ZipFile(TESTFN, "w", self.compression, + zipf = zipfile2.ZipFile(TESTFN, "w", self.compression, allowZip64=False) zipf.debug = 100 numfiles = 9 @@ -784,7 +786,7 @@ def test_too_many_files_append(self): self.assertEqual(len(zipf.namelist()), numfiles) zipf.close() - zipf = zipfile.ZipFile(TESTFN, "a", self.compression, + zipf = zipfile2.ZipFile(TESTFN, "a", self.compression, allowZip64=False) zipf.debug = 100 self.assertEqual(len(zipf.namelist()), numfiles) @@ -793,7 +795,7 @@ def test_too_many_files_append(self): self.assertEqual(len(zipf.namelist()), numfiles) zipf.close() - zipf = zipfile.ZipFile(TESTFN, "a", self.compression, + zipf = zipfile2.ZipFile(TESTFN, "a", self.compression, allowZip64=True) zipf.debug = 100 self.assertEqual(len(zipf.namelist()), numfiles) @@ -803,7 +805,7 @@ def test_too_many_files_append(self): self.assertEqual(len(zipf.namelist()), numfiles2) zipf.close() - zipf2 = zipfile.ZipFile(TESTFN, "r", self.compression) + zipf2 = zipfile2.ZipFile(TESTFN, "r", self.compression) self.assertEqual(len(zipf2.namelist()), numfiles2) for i in range(numfiles2): content = zipf2.read("foo%08d" % i).decode('ascii') @@ -822,12 +824,12 @@ class StoredTestZip64InSmallFiles(AbstractTestZip64InSmallFiles, compression = zipfile.ZIP_STORED def large_file_exception_test(self, f, compression): - with zipfile.ZipFile(f, "w", compression, allowZip64=False) as zipfp: + with zipfile2.ZipFile(f, "w", compression, allowZip64=False) as zipfp: self.assertRaises(zipfile.LargeZipFile, zipfp.write, TESTFN, "another.name") def large_file_exception_test2(self, f, compression): - with zipfile.ZipFile(f, "w", compression, allowZip64=False) as zipfp: + with zipfile2.ZipFile(f, "w", compression, allowZip64=False) as zipfp: self.assertRaises(zipfile.LargeZipFile, zipfp.writestr, "another.name", self.data) @@ -837,24 +839,24 @@ def test_large_file_exception(self): self.large_file_exception_test2(f, zipfile.ZIP_STORED) def test_absolute_arcnames(self): - with zipfile.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED, + with zipfile2.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED, allowZip64=True) as zipfp: zipfp.write(TESTFN, "/absolute") - with zipfile.ZipFile(TESTFN2, "r", zipfile.ZIP_STORED) as zipfp: + with zipfile2.ZipFile(TESTFN2, "r", zipfile.ZIP_STORED) as zipfp: self.assertEqual(zipfp.namelist(), ["absolute"]) def test_append(self): # Test that appending to the Zip64 archive doesn't change # extra fields of existing entries. - with zipfile.ZipFile(TESTFN2, "w", allowZip64=True) as zipfp: + with zipfile2.ZipFile(TESTFN2, "w", allowZip64=True) as zipfp: zipfp.writestr("strfile", self.data) - with zipfile.ZipFile(TESTFN2, "r", allowZip64=True) as zipfp: + with zipfile2.ZipFile(TESTFN2, "r", allowZip64=True) as zipfp: zinfo = zipfp.getinfo("strfile") extra = zinfo.extra - with zipfile.ZipFile(TESTFN2, "a", allowZip64=True) as zipfp: + with zipfile2.ZipFile(TESTFN2, "a", allowZip64=True) as zipfp: zipfp.writestr("strfile2", self.data) - with zipfile.ZipFile(TESTFN2, "r", allowZip64=True) as zipfp: + with zipfile2.ZipFile(TESTFN2, "r", allowZip64=True) as zipfp: zinfo = zipfp.getinfo("strfile") self.assertEqual(zinfo.extra, extra) @@ -979,7 +981,7 @@ def test_bad_zip64_extra(self): file_size_64_set=True, ) with self.assertRaises(zipfile.BadZipFile) as e: - zipfile.ZipFile(io.BytesIO(missing_file_size_extra)) + zipfile2.ZipFile(io.BytesIO(missing_file_size_extra)) self.assertIn('file size', str(e.exception).lower()) # zip64 file size present, zip64 compress size present, one field in @@ -990,7 +992,7 @@ def test_bad_zip64_extra(self): compress_size_64_set=True, ) with self.assertRaises(zipfile.BadZipFile) as e: - zipfile.ZipFile(io.BytesIO(missing_compress_size_extra)) + zipfile2.ZipFile(io.BytesIO(missing_compress_size_extra)) self.assertIn('compress size', str(e.exception).lower()) # zip64 compress size present, no fields in extra, expecting one, @@ -999,7 +1001,7 @@ def test_bad_zip64_extra(self): compress_size_64_set=True, ) with self.assertRaises(zipfile.BadZipFile) as e: - zipfile.ZipFile(io.BytesIO(missing_compress_size_extra)) + zipfile2.ZipFile(io.BytesIO(missing_compress_size_extra)) self.assertIn('compress size', str(e.exception).lower()) # zip64 file size present, zip64 compress size present, zip64 header @@ -1013,7 +1015,7 @@ def test_bad_zip64_extra(self): header_offset_64_set=True, ) with self.assertRaises(zipfile.BadZipFile) as e: - zipfile.ZipFile(io.BytesIO(missing_header_offset_extra)) + zipfile2.ZipFile(io.BytesIO(missing_header_offset_extra)) self.assertIn('header offset', str(e.exception).lower()) # zip64 compress size present, zip64 header offset present, one field @@ -1025,7 +1027,7 @@ def test_bad_zip64_extra(self): header_offset_64_set=True, ) with self.assertRaises(zipfile.BadZipFile) as e: - zipfile.ZipFile(io.BytesIO(missing_header_offset_extra)) + zipfile2.ZipFile(io.BytesIO(missing_header_offset_extra)) self.assertIn('header offset', str(e.exception).lower()) # zip64 file size present, zip64 header offset present, one field in @@ -1037,7 +1039,7 @@ def test_bad_zip64_extra(self): header_offset_64_set=True, ) with self.assertRaises(zipfile.BadZipFile) as e: - zipfile.ZipFile(io.BytesIO(missing_header_offset_extra)) + zipfile2.ZipFile(io.BytesIO(missing_header_offset_extra)) self.assertIn('header offset', str(e.exception).lower()) # zip64 header offset present, no fields in extra, expecting one, @@ -1048,7 +1050,7 @@ def test_bad_zip64_extra(self): header_offset_64_set=True, ) with self.assertRaises(zipfile.BadZipFile) as e: - zipfile.ZipFile(io.BytesIO(missing_header_offset_extra)) + zipfile2.ZipFile(io.BytesIO(missing_header_offset_extra)) self.assertIn('header offset', str(e.exception).lower()) def test_generated_valid_zip64_extra(self): @@ -1071,7 +1073,7 @@ def test_generated_valid_zip64_extra(self): kwargs = {} for c in combo: kwargs.update(c) - with zipfile.ZipFile(io.BytesIO(self.make_zip64_file(**kwargs))) as zf: + with zipfile2.ZipFile(io.BytesIO(self.make_zip64_file(**kwargs))) as zf: zinfo = zf.infolist()[0] self.assertEqual(zinfo.file_size, expected_file_size) self.assertEqual(zinfo.compress_size, expected_compress_size) @@ -1102,7 +1104,7 @@ def tearDown(self): def test_close_after_close(self): data = b'content' - with zipfile.ZipFile(TESTFN2, "w", self.compression) as zipf: + with zipfile2.ZipFile(TESTFN2, "w", self.compression) as zipf: w = zipf.open('test', 'w') w.write(data) w.close() @@ -1113,7 +1115,7 @@ def test_close_after_close(self): def test_write_after_close(self): data = b'content' - with zipfile.ZipFile(TESTFN2, "w", self.compression) as zipf: + with zipfile2.ZipFile(TESTFN2, "w", self.compression) as zipf: w = zipf.open('test', 'w') w.write(data) w.close() @@ -1124,7 +1126,7 @@ def test_write_after_close(self): def test_issue44439(self): q = array.array('Q', [1, 2, 3, 4, 5]) LENGTH = len(q) * q.itemsize - with zipfile.ZipFile(io.BytesIO(), 'w', self.compression) as zip: + with zipfile2.ZipFile(io.BytesIO(), 'w', self.compression) as zip: with zip.open('data', 'w') as data: self.assertEqual(data.write(q), LENGTH) self.assertEqual(zip.getinfo('data').file_size, LENGTH) @@ -1341,14 +1343,14 @@ def test_write_pathlike(self): class ExtractTests(unittest.TestCase): def make_test_file(self): - with zipfile.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED) as zipfp: + with zipfile2.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED) as zipfp: for fpath, fdata in SMALL_TEST_DATA: zipfp.writestr(fpath, fdata) def test_extract(self): with temp_cwd(): self.make_test_file() - with zipfile.ZipFile(TESTFN2, "r") as zipfp: + with zipfile2.ZipFile(TESTFN2, "r") as zipfp: for fpath, fdata in SMALL_TEST_DATA: writtenfile = zipfp.extract(fpath) @@ -1366,7 +1368,7 @@ def test_extract(self): def _test_extract_with_target(self, target): self.make_test_file() - with zipfile.ZipFile(TESTFN2, "r") as zipfp: + with zipfile2.ZipFile(TESTFN2, "r") as zipfp: for fpath, fdata in SMALL_TEST_DATA: writtenfile = zipfp.extract(fpath, target) @@ -1394,7 +1396,7 @@ def test_extract_with_target_pathlike(self): def test_extract_all(self): with temp_cwd(): self.make_test_file() - with zipfile.ZipFile(TESTFN2, "r") as zipfp: + with zipfile2.ZipFile(TESTFN2, "r") as zipfp: zipfp.extractall() for fpath, fdata in SMALL_TEST_DATA: outfile = os.path.join(os.getcwd(), fpath) @@ -1406,7 +1408,7 @@ def test_extract_all(self): def _test_extract_all_with_target(self, target): self.make_test_file() - with zipfile.ZipFile(TESTFN2, "r") as zipfp: + with zipfile2.ZipFile(TESTFN2, "r") as zipfp: zipfp.extractall(target) for fpath, fdata in SMALL_TEST_DATA: outfile = os.path.join(target, fpath) @@ -1432,7 +1434,7 @@ def check_file(self, filename, content): self.assertEqual(f.read(), content) def test_sanitize_windows_name(self): - san = zipfile.ZipFile._sanitize_windows_name + san = zipfile2.ZipFile._sanitize_windows_name # Passing pathsep in allows this test to work regardless of platform. self.assertEqual(san(r',,?,C:,foo,bar/z', ','), r'_,C_,foo,bar/z') self.assertEqual(san(r'a\b,ce|f"g?h*i', ','), r'a\b,c_d_e_f_g_h_i') @@ -1489,7 +1491,7 @@ def test_extract_hackers_arcnames_posix_only(self): def _test_extract_hackers_arcnames(self, hacknames): for arcname, fixedname in hacknames: content = b'foobar' + arcname.encode() - with zipfile.ZipFile(TESTFN2, 'w', zipfile.ZIP_STORED) as zipfp: + with zipfile2.ZipFile(TESTFN2, 'w', zipfile.ZIP_STORED) as zipfp: zinfo = zipfile.ZipInfo() # preserve backslashes zinfo.filename = arcname @@ -1500,7 +1502,7 @@ def _test_extract_hackers_arcnames(self, hacknames): targetpath = os.path.join('target', 'subdir', 'subsub') correctfile = os.path.join(targetpath, *fixedname.split('/')) - with zipfile.ZipFile(TESTFN2, 'r') as zipfp: + with zipfile2.ZipFile(TESTFN2, 'r') as zipfp: writtenfile = zipfp.extract(arcname, targetpath) self.assertEqual(writtenfile, correctfile, msg='extract %r: %r != %r' % @@ -1508,21 +1510,21 @@ def _test_extract_hackers_arcnames(self, hacknames): self.check_file(correctfile, content) rmtree('target') - with zipfile.ZipFile(TESTFN2, 'r') as zipfp: + with zipfile2.ZipFile(TESTFN2, 'r') as zipfp: zipfp.extractall(targetpath) self.check_file(correctfile, content) rmtree('target') correctfile = os.path.join(os.getcwd(), *fixedname.split('/')) - with zipfile.ZipFile(TESTFN2, 'r') as zipfp: + with zipfile2.ZipFile(TESTFN2, 'r') as zipfp: writtenfile = zipfp.extract(arcname) self.assertEqual(writtenfile, correctfile, msg="extract %r" % arcname) self.check_file(correctfile, content) rmtree(fixedname.split('/')[0]) - with zipfile.ZipFile(TESTFN2, 'r') as zipfp: + with zipfile2.ZipFile(TESTFN2, 'r') as zipfp: zipfp.extractall() self.check_file(correctfile, content) rmtree(fixedname.split('/')[0]) @@ -1533,13 +1535,13 @@ def _test_extract_hackers_arcnames(self, hacknames): class OtherTests(unittest.TestCase): def test_open_via_zip_info(self): # Create the ZIP archive - with zipfile.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED) as zipfp: + with zipfile2.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED) as zipfp: zipfp.writestr("name", "foo") with self.assertWarns(UserWarning): zipfp.writestr("name", "bar") self.assertEqual(zipfp.namelist(), ["name"] * 2) - with zipfile.ZipFile(TESTFN2, "r") as zipfp: + with zipfile2.ZipFile(TESTFN2, "r") as zipfp: infos = zipfp.infolist() data = b"" for info in infos: @@ -1552,7 +1554,7 @@ def test_open_via_zip_info(self): self.assertIn(data, {b"foobar", b"barfoo"}) def test_writestr_extended_local_header_issue1202(self): - with zipfile.ZipFile(TESTFN2, 'w') as orig_zip: + with zipfile2.ZipFile(TESTFN2, 'w') as orig_zip: for data in 'abcdefghijklmnop': zinfo = zipfile.ZipInfo(data) zinfo.flag_bits |= 0x08 # Include an extended local header. @@ -1560,25 +1562,25 @@ def test_writestr_extended_local_header_issue1202(self): def test_close(self): """Check that the zipfile is closed after the 'with' block.""" - with zipfile.ZipFile(TESTFN2, "w") as zipfp: + with zipfile2.ZipFile(TESTFN2, "w") as zipfp: for fpath, fdata in SMALL_TEST_DATA: zipfp.writestr(fpath, fdata) self.assertIsNotNone(zipfp.fp, 'zipfp is not open') self.assertIsNone(zipfp.fp, 'zipfp is not closed') - with zipfile.ZipFile(TESTFN2, "r") as zipfp: + with zipfile2.ZipFile(TESTFN2, "r") as zipfp: self.assertIsNotNone(zipfp.fp, 'zipfp is not open') self.assertIsNone(zipfp.fp, 'zipfp is not closed') def test_close_on_exception(self): """Check that the zipfile is closed if an exception is raised in the 'with' block.""" - with zipfile.ZipFile(TESTFN2, "w") as zipfp: + with zipfile2.ZipFile(TESTFN2, "w") as zipfp: for fpath, fdata in SMALL_TEST_DATA: zipfp.writestr(fpath, fdata) try: - with zipfile.ZipFile(TESTFN2, "r") as zipfp2: + with zipfile2.ZipFile(TESTFN2, "r") as zipfp2: raise zipfile.BadZipFile() except zipfile.BadZipFile: self.assertIsNone(zipfp2.fp, 'zipfp is not closed') @@ -1591,29 +1593,29 @@ def test_unsupported_version(self): b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x01\x00\x00\x00\x00xPK\x05\x06' b'\x00\x00\x00\x00\x01\x00\x01\x00/\x00\x00\x00\x1f\x00\x00\x00\x00\x00') - self.assertRaises(NotImplementedError, zipfile.ZipFile, + self.assertRaises(NotImplementedError, zipfile2.ZipFile, io.BytesIO(data), 'r') @requires_zlib() def test_read_unicode_filenames(self): # bug #10801 fname = findfile('zip_cp437_header.zip') - with zipfile.ZipFile(fname) as zipfp: + with zipfile2.ZipFile(fname) as zipfp: for name in zipfp.namelist(): zipfp.open(name).close() def test_write_unicode_filenames(self): - with zipfile.ZipFile(TESTFN, "w") as zf: + with zipfile2.ZipFile(TESTFN, "w") as zf: zf.writestr("foo.txt", "Test for unicode filename") zf.writestr("\xf6.txt", "Test for unicode filename") self.assertIsInstance(zf.infolist()[0].filename, str) - with zipfile.ZipFile(TESTFN, "r") as zf: + with zipfile2.ZipFile(TESTFN, "r") as zf: self.assertEqual(zf.filelist[0].filename, "foo.txt") self.assertEqual(zf.filelist[1].filename, "\xf6.txt") def test_read_after_write_unicode_filenames(self): - with zipfile.ZipFile(TESTFN2, 'w') as zipfp: + with zipfile2.ZipFile(TESTFN2, 'w') as zipfp: zipfp.writestr('приклад', b'sample') self.assertEqual(zipfp.read('приклад'), b'sample') @@ -1622,11 +1624,11 @@ def test_exclusive_create_zip_file(self): unlink(TESTFN2) filename = 'testfile.txt' content = b'hello, world. this is some content.' - with zipfile.ZipFile(TESTFN2, "x", zipfile.ZIP_STORED) as zipfp: + with zipfile2.ZipFile(TESTFN2, "x", zipfile.ZIP_STORED) as zipfp: zipfp.writestr(filename, content) with self.assertRaises(FileExistsError): - zipfile.ZipFile(TESTFN2, "x", zipfile.ZIP_STORED) - with zipfile.ZipFile(TESTFN2, "r") as zipfp: + zipfile2.ZipFile(TESTFN2, "x", zipfile.ZIP_STORED) + with zipfile2.ZipFile(TESTFN2, "r") as zipfp: self.assertEqual(zipfp.namelist(), [filename]) self.assertEqual(zipfp.read(filename), content) @@ -1638,14 +1640,14 @@ def test_create_non_existent_file_for_append(self): content = b'hello, world. this is some content.' try: - with zipfile.ZipFile(TESTFN, 'a') as zf: + with zipfile2.ZipFile(TESTFN, 'a') as zf: zf.writestr(filename, content) except OSError: self.fail('Could not append data to a non-existent zip file.') self.assertTrue(os.path.exists(TESTFN)) - with zipfile.ZipFile(TESTFN, 'r') as zf: + with zipfile2.ZipFile(TESTFN, 'r') as zf: self.assertEqual(zf.read(filename), content) def test_close_erroneous_file(self): @@ -1659,7 +1661,7 @@ def test_close_erroneous_file(self): with open(TESTFN, "w", encoding="utf-8") as fp: fp.write("this is not a legal zip file\n") try: - zf = zipfile.ZipFile(TESTFN) + zf = zipfile2.ZipFile(TESTFN) except zipfile.BadZipFile: pass @@ -1685,7 +1687,7 @@ def test_damaged_zipfile(self): """Check that zipfiles with missing bytes at the end raise BadZipFile.""" # - Create a valid zip file fp = io.BytesIO() - with zipfile.ZipFile(fp, mode="w") as zipf: + with zipfile2.ZipFile(fp, mode="w") as zipf: zipf.writestr("foo.txt", b"O, for a Muse of Fire!") zipfiledata = fp.getvalue() @@ -1693,12 +1695,12 @@ def test_damaged_zipfile(self): # a BadZipFile exception is raised when we try to open it for N in range(len(zipfiledata)): fp = io.BytesIO(zipfiledata[:N]) - self.assertRaises(zipfile.BadZipFile, zipfile.ZipFile, fp) + self.assertRaises(zipfile.BadZipFile, zipfile2.ZipFile, fp) def test_is_zip_valid_file(self): """Check that is_zipfile() correctly identifies zip files.""" # - passing a filename - with zipfile.ZipFile(TESTFN, mode="w") as zipf: + with zipfile2.ZipFile(TESTFN, mode="w") as zipf: zipf.writestr("foo.txt", b"O, for a Muse of Fire!") self.assertTrue(zipfile.is_zipfile(TESTFN)) @@ -1726,16 +1728,16 @@ def test_non_existent_file_raises_OSError(self): # it is ignored, but the user should be sufficiently annoyed by # the message on the output that regression will be noticed # quickly. - self.assertRaises(OSError, zipfile.ZipFile, TESTFN) + self.assertRaises(OSError, zipfile2.ZipFile, TESTFN) def test_empty_file_raises_BadZipFile(self): f = open(TESTFN, 'w', encoding='utf-8') f.close() - self.assertRaises(zipfile.BadZipFile, zipfile.ZipFile, TESTFN) + self.assertRaises(zipfile.BadZipFile, zipfile2.ZipFile, TESTFN) with open(TESTFN, 'w', encoding='utf-8') as fp: fp.write("short file") - self.assertRaises(zipfile.BadZipFile, zipfile.ZipFile, TESTFN) + self.assertRaises(zipfile.BadZipFile, zipfile2.ZipFile, TESTFN) def test_negative_central_directory_offset_raises_BadZipFile(self): # Zip file containing an empty EOCD record @@ -1746,12 +1748,12 @@ def test_negative_central_directory_offset_raises_BadZipFile(self): for dirsize in 1, 2**32-1: buffer[12:16] = struct.pack(' os.path.getsize(TESTFN)) - with zipfile.ZipFile(TESTFN,mode="r") as zipf: + with zipfile2.ZipFile(TESTFN,mode="r") as zipf: self.assertEqual(zipf.comment, b"shorter comment") def test_unicode_comment(self): - with zipfile.ZipFile(TESTFN, "w", zipfile.ZIP_STORED) as zipf: + with zipfile2.ZipFile(TESTFN, "w", zipfile.ZIP_STORED) as zipf: zipf.writestr("foo.txt", "O, for a Muse of Fire!") with self.assertRaises(TypeError): zipf.comment = "this is an error" def test_change_comment_in_empty_archive(self): - with zipfile.ZipFile(TESTFN, "a", zipfile.ZIP_STORED) as zipf: + with zipfile2.ZipFile(TESTFN, "a", zipfile.ZIP_STORED) as zipf: self.assertFalse(zipf.filelist) zipf.comment = b"this is a comment" - with zipfile.ZipFile(TESTFN, "r") as zipf: + with zipfile2.ZipFile(TESTFN, "r") as zipf: self.assertEqual(zipf.comment, b"this is a comment") def test_change_comment_in_nonempty_archive(self): - with zipfile.ZipFile(TESTFN, "w", zipfile.ZIP_STORED) as zipf: + with zipfile2.ZipFile(TESTFN, "w", zipfile.ZIP_STORED) as zipf: zipf.writestr("foo.txt", "O, for a Muse of Fire!") - with zipfile.ZipFile(TESTFN, "a", zipfile.ZIP_STORED) as zipf: + with zipfile2.ZipFile(TESTFN, "a", zipfile.ZIP_STORED) as zipf: self.assertTrue(zipf.filelist) zipf.comment = b"this is a comment" - with zipfile.ZipFile(TESTFN, "r") as zipf: + with zipfile2.ZipFile(TESTFN, "r") as zipf: self.assertEqual(zipf.comment, b"this is a comment") def test_empty_zipfile(self): # Check that creating a file in 'w' or 'a' mode and closing without # adding any files to the archives creates a valid empty ZIP file - zipf = zipfile.ZipFile(TESTFN, mode="w") + zipf = zipfile2.ZipFile(TESTFN, mode="w") zipf.close() try: - zipf = zipfile.ZipFile(TESTFN, mode="r") + zipf = zipfile2.ZipFile(TESTFN, mode="r") except zipfile.BadZipFile: self.fail("Unable to create empty ZIP file in 'w' mode") - zipf = zipfile.ZipFile(TESTFN, mode="a") + zipf = zipfile2.ZipFile(TESTFN, mode="a") zipf.close() try: - zipf = zipfile.ZipFile(TESTFN, mode="r") + zipf = zipfile2.ZipFile(TESTFN, mode="r") except: self.fail("Unable to create empty ZIP file in 'a' mode") @@ -1934,7 +1936,7 @@ def test_open_empty_file(self): # OSError) f = open(TESTFN, 'w', encoding='utf-8') f.close() - self.assertRaises(zipfile.BadZipFile, zipfile.ZipFile, TESTFN, 'r') + self.assertRaises(zipfile.BadZipFile, zipfile2.ZipFile, TESTFN, 'r') def test_create_zipinfo_before_1980(self): self.assertRaises(ValueError, @@ -1978,7 +1980,7 @@ def test_zipfile_with_short_extra_field(self): b'\x00\x00\x00abc\x00\x00PK\x05\x06\x00\x00\x00\x00' b'\x01\x00\x01\x003\x00\x00\x00%\x00\x00\x00\x00\x00' ) - with zipfile.ZipFile(io.BytesIO(zipdata), 'r') as zipf: + with zipfile2.ZipFile(io.BytesIO(zipdata), 'r') as zipf: # testzip returns the name of the first corrupt file, or None self.assertIsNone(zipf.testzip()) @@ -1987,7 +1989,7 @@ def test_open_conflicting_handles(self): msg1 = b"It's fun to charter an accountant!" msg2 = b"And sail the wide accountant sea" msg3 = b"To find, explore the funds offshore" - with zipfile.ZipFile(TESTFN2, 'w', zipfile.ZIP_STORED) as zipf: + with zipfile2.ZipFile(TESTFN2, 'w', zipfile.ZIP_STORED) as zipf: with zipf.open('foo', mode='w') as w2: w2.write(msg1) with zipf.open('bar', mode='w') as w1: @@ -2005,7 +2007,7 @@ def test_open_conflicting_handles(self): with zipf.open('baz', mode='w') as w2: w2.write(msg3) - with zipfile.ZipFile(TESTFN2, 'r') as zipf: + with zipfile2.ZipFile(TESTFN2, 'r') as zipf: self.assertEqual(zipf.read('foo'), msg1) self.assertEqual(zipf.read('bar'), msg2) self.assertEqual(zipf.read('baz'), msg3) @@ -2016,9 +2018,9 @@ def test_seek_tell(self): txt = b"Where's Bruce?" bloc = txt.find(b"Bruce") # Check seek on a file - with zipfile.ZipFile(TESTFN, "w") as zipf: + with zipfile2.ZipFile(TESTFN, "w") as zipf: zipf.writestr("foo.txt", txt) - with zipfile.ZipFile(TESTFN, "r") as zipf: + with zipfile2.ZipFile(TESTFN, "r") as zipf: with zipf.open("foo.txt", "r") as fp: fp.seek(bloc, os.SEEK_SET) self.assertEqual(fp.tell(), bloc) @@ -2033,9 +2035,9 @@ def test_seek_tell(self): self.assertEqual(fp.tell(), 0) # Check seek on memory file data = io.BytesIO() - with zipfile.ZipFile(data, mode="w") as zipf: + with zipfile2.ZipFile(data, mode="w") as zipf: zipf.writestr("foo.txt", txt) - with zipfile.ZipFile(data, mode="r") as zipf: + with zipfile2.ZipFile(data, mode="r") as zipf: with zipf.open("foo.txt", "r") as fp: fp.seek(bloc, os.SEEK_SET) self.assertEqual(fp.tell(), bloc) @@ -2053,10 +2055,10 @@ def test_seek_tell(self): def test_decompress_without_3rd_party_library(self): data = b'PK\x05\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' zip_file = io.BytesIO(data) - with zipfile.ZipFile(zip_file, 'w', compression=zipfile.ZIP_BZIP2) as zf: + with zipfile2.ZipFile(zip_file, 'w', compression=zipfile.ZIP_BZIP2) as zf: zf.writestr('a.txt', b'a') with mock.patch('zipfile.bz2', None): - with zipfile.ZipFile(zip_file) as zf: + with zipfile2.ZipFile(zip_file) as zf: self.assertRaises(RuntimeError, zf.extract, 'a.txt') @requires_zlib() @@ -2074,7 +2076,7 @@ def test_full_overlap(self): b'\x06\x00\x00\x00\x00\x02\x00\x02\x00^\x00\x00\x00/\x00\x00' b'\x00\x00\x00' ) - with zipfile.ZipFile(io.BytesIO(data), 'r') as zipf: + with zipfile2.ZipFile(io.BytesIO(data), 'r') as zipf: self.assertEqual(zipf.namelist(), ['a', 'b']) zi = zipf.getinfo('a') self.assertEqual(zi.header_offset, 0) @@ -2105,7 +2107,7 @@ def test_quoted_overlap(self): b'bPK\x05\x06\x00\x00\x00\x00\x02\x00\x02\x00^\x00\x00' b'\x00S\x00\x00\x00\x00\x00' ) - with zipfile.ZipFile(io.BytesIO(data), 'r') as zipf: + with zipfile2.ZipFile(io.BytesIO(data), 'r') as zipf: self.assertEqual(zipf.namelist(), ['a', 'b']) zi = zipf.getinfo('a') self.assertEqual(zi.header_offset, 0) @@ -2129,7 +2131,7 @@ def test_testzip_with_bad_crc(self): """Tests that files with bad CRCs return their name from testzip.""" zipdata = self.zip_with_bad_crc - with zipfile.ZipFile(io.BytesIO(zipdata), mode="r") as zipf: + with zipfile2.ZipFile(io.BytesIO(zipdata), mode="r") as zipf: # testzip returns the name of the first corrupt file, or None self.assertEqual('afile', zipf.testzip()) @@ -2138,16 +2140,16 @@ def test_read_with_bad_crc(self): zipdata = self.zip_with_bad_crc # Using ZipFile.read() - with zipfile.ZipFile(io.BytesIO(zipdata), mode="r") as zipf: + with zipfile2.ZipFile(io.BytesIO(zipdata), mode="r") as zipf: self.assertRaises(zipfile.BadZipFile, zipf.read, 'afile') # Using ZipExtFile.read() - with zipfile.ZipFile(io.BytesIO(zipdata), mode="r") as zipf: + with zipfile2.ZipFile(io.BytesIO(zipdata), mode="r") as zipf: with zipf.open('afile', 'r') as corrupt_file: self.assertRaises(zipfile.BadZipFile, corrupt_file.read) # Same with small reads (in order to exercise the buffering logic) - with zipfile.ZipFile(io.BytesIO(zipdata), mode="r") as zipf: + with zipfile2.ZipFile(io.BytesIO(zipdata), mode="r") as zipf: with zipf.open('afile', 'r') as corrupt_file: corrupt_file.MIN_READ_SIZE = 2 with self.assertRaises(zipfile.BadZipFile): @@ -2240,10 +2242,10 @@ class DecryptionTests(unittest.TestCase): def setUp(self): with open(TESTFN, "wb") as fp: fp.write(self.data) - self.zip = zipfile.ZipFile(TESTFN, "r") + self.zip = zipfile2.ZipFile(TESTFN, "r") with open(TESTFN2, "wb") as fp: fp.write(self.data2) - self.zip2 = zipfile.ZipFile(TESTFN2, "r") + self.zip2 = zipfile2.ZipFile(TESTFN2, "r") def tearDown(self): self.zip.close() @@ -2332,7 +2334,7 @@ def tearDown(self): def make_test_archive(self, f, compression): # Create the ZIP archive - with zipfile.ZipFile(f, "w", compression) as zipfp: + with zipfile2.ZipFile(f, "w", compression) as zipfp: zipfp.write(TESTFN, "another.name") zipfp.write(TESTFN, TESTFN) @@ -2340,7 +2342,7 @@ def zip_test(self, f, compression): self.make_test_archive(f, compression) # Read the ZIP archive - with zipfile.ZipFile(f, "r", compression) as zipfp: + with zipfile2.ZipFile(f, "r", compression) as zipfp: testdata = zipfp.read(TESTFN) self.assertEqual(len(testdata), len(self.data)) self.assertEqual(testdata, self.data) @@ -2354,7 +2356,7 @@ def zip_open_test(self, f, compression): self.make_test_archive(f, compression) # Read the ZIP archive - with zipfile.ZipFile(f, "r", compression) as zipfp: + with zipfile2.ZipFile(f, "r", compression) as zipfp: zipdata1 = [] with zipfp.open(TESTFN) as zipopen1: while True: @@ -2387,7 +2389,7 @@ def zip_random_open_test(self, f, compression): self.make_test_archive(f, compression) # Read the ZIP archive - with zipfile.ZipFile(f, "r", compression) as zipfp: + with zipfile2.ZipFile(f, "r", compression) as zipfp: zipdata1 = [] with zipfp.open(TESTFN) as zipopen1: while True: @@ -2459,11 +2461,11 @@ def test_writestr(self): f = io.BytesIO() f.write(b'abc') bf = io.BufferedWriter(f) - with zipfile.ZipFile(wrapper(bf), 'w', zipfile.ZIP_STORED) as zipfp: + with zipfile2.ZipFile(wrapper(bf), 'w', zipfile.ZIP_STORED) as zipfp: zipfp.writestr('ones', b'111') zipfp.writestr('twos', b'222') self.assertEqual(f.getvalue()[:5], b'abcPK') - with zipfile.ZipFile(f, mode='r') as zipf: + with zipfile2.ZipFile(f, mode='r') as zipf: with zipf.open('ones') as zopen: self.assertEqual(zopen.read(), b'111') with zipf.open('twos') as zopen: @@ -2475,7 +2477,7 @@ def test_write(self): f = io.BytesIO() f.write(b'abc') bf = io.BufferedWriter(f) - with zipfile.ZipFile(wrapper(bf), 'w', zipfile.ZIP_STORED) as zipfp: + with zipfile2.ZipFile(wrapper(bf), 'w', zipfile.ZIP_STORED) as zipfp: self.addCleanup(unlink, TESTFN) with open(TESTFN, 'wb') as f2: f2.write(b'111') @@ -2484,7 +2486,7 @@ def test_write(self): f2.write(b'222') zipfp.write(TESTFN, 'twos') self.assertEqual(f.getvalue()[:5], b'abcPK') - with zipfile.ZipFile(f, mode='r') as zipf: + with zipfile2.ZipFile(f, mode='r') as zipf: with zipf.open('ones') as zopen: self.assertEqual(zopen.read(), b'111') with zipf.open('twos') as zopen: @@ -2496,13 +2498,13 @@ def test_open_write(self): f = io.BytesIO() f.write(b'abc') bf = io.BufferedWriter(f) - with zipfile.ZipFile(wrapper(bf), 'w', zipfile.ZIP_STORED) as zipf: + with zipfile2.ZipFile(wrapper(bf), 'w', zipfile.ZIP_STORED) as zipf: with zipf.open('ones', 'w') as zopen: zopen.write(b'111') with zipf.open('twos', 'w') as zopen: zopen.write(b'222') self.assertEqual(f.getvalue()[:5], b'abcPK') - with zipfile.ZipFile(f) as zipf: + with zipfile2.ZipFile(f) as zipf: self.assertEqual(zipf.read('ones'), b'111') self.assertEqual(zipf.read('twos'), b'222') @@ -2516,7 +2518,7 @@ def setUpClass(cls): def make_test_archive(self, f): # Create the ZIP archive - with zipfile.ZipFile(f, "w", zipfile.ZIP_DEFLATED) as zipfp: + with zipfile2.ZipFile(f, "w", zipfile.ZIP_DEFLATED) as zipfp: zipfp.writestr('ones', self.data1) zipfp.writestr('twos', self.data2) @@ -2525,7 +2527,7 @@ def test_same_file(self): # multiple open() calls can be made without interfering with each other. for f in get_files(self): self.make_test_archive(f) - with zipfile.ZipFile(f, mode="r") as zipf: + with zipfile2.ZipFile(f, mode="r") as zipf: with zipf.open('ones') as zopen1, zipf.open('ones') as zopen2: data1 = zopen1.read(500) data2 = zopen2.read(500) @@ -2539,7 +2541,7 @@ def test_different_file(self): # multiple open() calls can be made without interfering with each other. for f in get_files(self): self.make_test_archive(f) - with zipfile.ZipFile(f, mode="r") as zipf: + with zipfile2.ZipFile(f, mode="r") as zipf: with zipf.open('ones') as zopen1, zipf.open('twos') as zopen2: data1 = zopen1.read(500) data2 = zopen2.read(500) @@ -2553,7 +2555,7 @@ def test_interleaved(self): # multiple open() calls can be made without interfering with each other. for f in get_files(self): self.make_test_archive(f) - with zipfile.ZipFile(f, mode="r") as zipf: + with zipfile2.ZipFile(f, mode="r") as zipf: with zipf.open('ones') as zopen1: data1 = zopen1.read(500) with zipf.open('twos') as zopen2: @@ -2567,7 +2569,7 @@ def test_read_after_close(self): for f in get_files(self): self.make_test_archive(f) with contextlib.ExitStack() as stack: - with zipfile.ZipFile(f, 'r') as zipf: + with zipfile2.ZipFile(f, 'r') as zipf: zopen1 = stack.enter_context(zipf.open('ones')) zopen2 = stack.enter_context(zipf.open('twos')) data1 = zopen1.read(500) @@ -2579,13 +2581,13 @@ def test_read_after_close(self): def test_read_after_write(self): for f in get_files(self): - with zipfile.ZipFile(f, 'w', zipfile.ZIP_DEFLATED) as zipf: + with zipfile2.ZipFile(f, 'w', zipfile.ZIP_DEFLATED) as zipf: zipf.writestr('ones', self.data1) zipf.writestr('twos', self.data2) with zipf.open('ones') as zopen1: data1 = zopen1.read(500) self.assertEqual(data1, self.data1[:500]) - with zipfile.ZipFile(f, 'r') as zipf: + with zipfile2.ZipFile(f, 'r') as zipf: data1 = zipf.read('ones') data2 = zipf.read('twos') self.assertEqual(data1, self.data1) @@ -2593,12 +2595,12 @@ def test_read_after_write(self): def test_write_after_read(self): for f in get_files(self): - with zipfile.ZipFile(f, "w", zipfile.ZIP_DEFLATED) as zipf: + with zipfile2.ZipFile(f, "w", zipfile.ZIP_DEFLATED) as zipf: zipf.writestr('ones', self.data1) with zipf.open('ones') as zopen1: zopen1.read(500) zipf.writestr('twos', self.data2) - with zipfile.ZipFile(f, 'r') as zipf: + with zipfile2.ZipFile(f, 'r') as zipf: data1 = zipf.read('ones') data2 = zipf.read('twos') self.assertEqual(data1, self.data1) @@ -2608,7 +2610,7 @@ def test_many_opens(self): # Verify that read() and open() promptly close the file descriptor, # and don't rely on the garbage collector to free resources. self.make_test_archive(TESTFN2) - with zipfile.ZipFile(TESTFN2, mode="r") as zipf: + with zipfile2.ZipFile(TESTFN2, mode="r") as zipf: for x in range(100): zipf.read('ones') with zipf.open('ones') as zopen1: @@ -2617,16 +2619,16 @@ def test_many_opens(self): self.assertLess(f.fileno(), 100) def test_write_while_reading(self): - with zipfile.ZipFile(TESTFN2, 'w', zipfile.ZIP_DEFLATED) as zipf: + with zipfile2.ZipFile(TESTFN2, 'w', zipfile.ZIP_DEFLATED) as zipf: zipf.writestr('ones', self.data1) - with zipfile.ZipFile(TESTFN2, 'a', zipfile.ZIP_DEFLATED) as zipf: + with zipfile2.ZipFile(TESTFN2, 'a', zipfile.ZIP_DEFLATED) as zipf: with zipf.open('ones', 'r') as r1: data1 = r1.read(500) with zipf.open('twos', 'w') as w1: w1.write(self.data2) data1 += r1.read() self.assertEqual(data1, self.data1) - with zipfile.ZipFile(TESTFN2) as zipf: + with zipfile2.ZipFile(TESTFN2) as zipf: self.assertEqual(zipf.read('twos'), self.data2) def tearDown(self): @@ -2638,7 +2640,7 @@ def setUp(self): os.mkdir(TESTFN2) def test_extract_dir(self): - with zipfile.ZipFile(findfile("zipdir.zip")) as zipf: + with zipfile2.ZipFile(findfile("zipdir.zip")) as zipf: zipf.extractall(TESTFN2) self.assertTrue(os.path.isdir(os.path.join(TESTFN2, "a"))) self.assertTrue(os.path.isdir(os.path.join(TESTFN2, "a", "b"))) @@ -2653,7 +2655,7 @@ def test_write_dir(self): dirpath = os.path.join(TESTFN2, "x") os.mkdir(dirpath) mode = os.stat(dirpath).st_mode & 0xFFFF - with zipfile.ZipFile(TESTFN, "w") as zipf: + with zipfile2.ZipFile(TESTFN, "w") as zipf: zipf.write(dirpath) zinfo = zipf.filelist[0] self.assertTrue(zinfo.filename.endswith("/x/")) @@ -2662,7 +2664,7 @@ def test_write_dir(self): zinfo = zipf.filelist[1] self.assertTrue(zinfo.filename, "y/") self.assertEqual(zinfo.external_attr, (mode << 16) | 0x10) - with zipfile.ZipFile(TESTFN, "r") as zipf: + with zipfile2.ZipFile(TESTFN, "r") as zipf: zinfo = zipf.filelist[0] self.assertTrue(zinfo.filename.endswith("/x/")) self.assertEqual(zinfo.external_attr, (mode << 16) | 0x10) @@ -2677,12 +2679,12 @@ def test_write_dir(self): def test_writestr_dir(self): os.mkdir(os.path.join(TESTFN2, "x")) - with zipfile.ZipFile(TESTFN, "w") as zipf: + with zipfile2.ZipFile(TESTFN, "w") as zipf: zipf.writestr("x/", b'') zinfo = zipf.filelist[0] self.assertEqual(zinfo.filename, "x/") self.assertEqual(zinfo.external_attr, (0o40775 << 16) | 0x10) - with zipfile.ZipFile(TESTFN, "r") as zipf: + with zipfile2.ZipFile(TESTFN, "r") as zipf: zinfo = zipf.filelist[0] self.assertTrue(zinfo.filename.endswith("x/")) self.assertEqual(zinfo.external_attr, (0o40775 << 16) | 0x10) @@ -2765,7 +2767,7 @@ def test_test_command(self): def test_list_command(self): zip_name = findfile('zipdir.zip') t = io.StringIO() - with zipfile.ZipFile(zip_name, 'r') as tf: + with zipfile2.ZipFile(zip_name, 'r') as tf: tf.printdir(t) expected = t.getvalue().encode('ascii', 'backslashreplace') for opt in '-l', '--list': @@ -2788,7 +2790,7 @@ def test_create_command(self): try: out = self.zipfilecmd(opt, TESTFN2, *files) self.assertEqual(out, b'') - with zipfile.ZipFile(TESTFN2) as zf: + with zipfile2.ZipFile(TESTFN2) as zf: self.assertEqual(zf.namelist(), namelist) self.assertEqual(zf.read(namelist[0]), b'test 1') self.assertEqual(zf.read(namelist[2]), b'test 2') @@ -2801,7 +2803,7 @@ def test_extract_command(self): with temp_dir() as extdir: out = self.zipfilecmd(opt, zip_name, extdir) self.assertEqual(out, b'') - with zipfile.ZipFile(zip_name) as zf: + with zipfile2.ZipFile(zip_name) as zf: for zi in zf.infolist(): path = os.path.join(extdir, zi.filename.replace('/', os.sep)) @@ -2825,7 +2827,7 @@ def _test_zip_works(self, name): self.assertTrue(zipfile.is_zipfile(name), f'is_zipfile failed on {name}') # Ensure we can operate on these via ZipFile. - with zipfile.ZipFile(name) as zipfp: + with zipfile2.ZipFile(name) as zipfp: for n in zipfp.namelist(): data = zipfp.read(n) self.assertIn(b'FAVORITE_NUMBER', data) @@ -2908,7 +2910,7 @@ def build_alpharep_fixture(): "rep" because it's a representative example """ data = io.BytesIO() - zf = zipfile.ZipFile(data, "w") + zf = zipfile2.ZipFile(data, "w") zf.writestr("a.txt", b"content of a") zf.writestr("b/c.txt", b"content of c") zf.writestr("b/d/e.txt", b"content of e") @@ -3001,7 +3003,7 @@ def test_open(self, alpharep): def test_open_encoding_utf16(self): in_memory_file = io.BytesIO() - zf = zipfile.ZipFile(in_memory_file, "w") + zf = zipfile2.ZipFile(in_memory_file, "w") zf.writestr("path/16.txt", "This was utf-16".encode("utf-16")) zf.filename = "test_open_utf16.zip" root = zipfile.Path(zf) @@ -3016,7 +3018,7 @@ def test_open_encoding_utf16(self): def test_open_encoding_errors(self): in_memory_file = io.BytesIO() - zf = zipfile.ZipFile(in_memory_file, "w") + zf = zipfile2.ZipFile(in_memory_file, "w") zf.writestr("path/bad-utf8.bin", b"invalid utf-8: \xff\xff.") zf.filename = "test_read_text_encoding_errors.zip" root = zipfile.Path(zf) @@ -3043,7 +3045,7 @@ def test_encoding_warnings(self): """EncodingWarning must blame the read_text and open calls.""" code = '''\ import io, zipfile -with zipfile.ZipFile(io.BytesIO(), "w") as zf: +with zipfile2.ZipFile(io.BytesIO(), "w") as zf: zf.filename = '' zf.writestr("path/file.txt", b"Spanish Inquisition") root = zipfile.Path(zf) @@ -3063,7 +3065,7 @@ 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')) + zf = zipfile.Path(zipfile2.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', encoding="utf-8") as strm: @@ -3187,7 +3189,7 @@ def test_mutability(self, alpharep): def huge_zipfile(self): """Create a read-only zipfile with a huge number of entries entries.""" strm = io.BytesIO() - zf = zipfile.ZipFile(strm, "w") + zf = zipfile2.ZipFile(strm, "w") for entry in map(str, range(self.HUGE_ZIPFILE_NUM_ENTRIES)): zf.writestr(entry, entry) zf.mode = 'r' @@ -3212,7 +3214,7 @@ def test_implied_dirs_performance(self): @pass_alpharep def test_read_does_not_close(self, alpharep): alpharep = self.zipfile_ondisk(alpharep) - with zipfile.ZipFile(alpharep) as file: + with zipfile2.ZipFile(alpharep) as file: for rep in range(2): zipfile.Path(file, 'a.txt').read_text(encoding="utf-8") @@ -3275,7 +3277,7 @@ def test_extract_orig_with_implied_dirs(self, alpharep): A zip file wrapped in a Path should extract even with implied dirs. """ source_path = self.zipfile_ondisk(alpharep) - zf = zipfile.ZipFile(source_path) + zf = zipfile2.ZipFile(source_path) # wrap the zipfile for its side effect zipfile.Path(zf) zf.extractall(source_path.parent) @@ -3289,7 +3291,7 @@ def test_malformed_paths(self): Paths with dots are treated like regular files. """ data = io.BytesIO() - zf = zipfile.ZipFile(data, "w") + zf = zipfile2.ZipFile(data, "w") zf.writestr("/one-slash.txt", b"content") zf.writestr("//two-slash.txt", b"content") zf.writestr("../parent.txt", b"content") @@ -3307,7 +3309,7 @@ def test_unsupported_names(self): in the zip file. """ data = io.BytesIO() - zf = zipfile.ZipFile(data, "w") + zf = zipfile2.ZipFile(data, "w") zf.writestr("path?", b"content") zf.writestr("V: NMS.flac", b"fLaC...") zf.filename = '' @@ -3323,7 +3325,7 @@ def test_backslash_not_separator(self): In a zip file, backslashes are not separators. """ data = io.BytesIO() - zf = zipfile.ZipFile(data, "w") + zf = zipfile2.ZipFile(data, "w") zf.writestr(DirtyZipInfo.for_name("foo\\bar", zf), b"content") zf.filename = '' root = zipfile.Path(zf) diff --git a/cpython-tests/3.10/test_zipfile64.py b/cpython-tests/3.10/test_zipfile64.py index 0947013..8b511f8 100644 --- a/cpython-tests/3.10/test_zipfile64.py +++ b/cpython-tests/3.10/test_zipfile64.py @@ -20,6 +20,8 @@ from test.support import os_helper from test.support import requires_zlib +import zipfile2 + TESTFN = os_helper.TESTFN TESTFN2 = TESTFN + "2" @@ -38,7 +40,7 @@ def setUp(self): def zipTest(self, f, compression): # Create the ZIP archive. - with zipfile.ZipFile(f, "w", compression) as zipfp: + with zipfile2.ZipFile(f, "w", compression) as zipfp: # It will contain enough copies of self.data to reach about 6 GiB of # raw data to store. @@ -56,7 +58,7 @@ def zipTest(self, f, compression): sys.__stdout__.flush() # Read the ZIP archive - with zipfile.ZipFile(f, "r", compression) as zipfp: + with zipfile2.ZipFile(f, "r", compression) as zipfp: for num in range(filecount): self.assertEqual(zipfp.read("testfn%d" % num), self.data) # Print still working message since this test can be really slow @@ -94,21 +96,21 @@ class OtherTests(unittest.TestCase): def testMoreThan64kFiles(self): # This test checks that more than 64k files can be added to an archive, # and that the resulting archive can be read properly by ZipFile - with zipfile.ZipFile(TESTFN, mode="w", allowZip64=True) as zipf: + with zipfile2.ZipFile(TESTFN, mode="w", allowZip64=True) as zipf: zipf.debug = 100 numfiles = (1 << 16) * 3//2 for i in range(numfiles): zipf.writestr("foo%08d" % i, "%d" % (i**3 % 57)) self.assertEqual(len(zipf.namelist()), numfiles) - with zipfile.ZipFile(TESTFN, mode="r") as zipf2: + with zipfile2.ZipFile(TESTFN, mode="r") as zipf2: self.assertEqual(len(zipf2.namelist()), numfiles) for i in range(numfiles): content = zipf2.read("foo%08d" % i).decode('ascii') self.assertEqual(content, "%d" % (i**3 % 57)) def testMoreThan64kFilesAppend(self): - with zipfile.ZipFile(TESTFN, mode="w", allowZip64=False) as zipf: + with zipfile2.ZipFile(TESTFN, mode="w", allowZip64=False) as zipf: zipf.debug = 100 numfiles = (1 << 16) - 1 for i in range(numfiles): @@ -118,14 +120,14 @@ def testMoreThan64kFilesAppend(self): zipf.writestr("foo%08d" % numfiles, b'') self.assertEqual(len(zipf.namelist()), numfiles) - with zipfile.ZipFile(TESTFN, mode="a", allowZip64=False) as zipf: + with zipfile2.ZipFile(TESTFN, mode="a", allowZip64=False) as zipf: zipf.debug = 100 self.assertEqual(len(zipf.namelist()), numfiles) with self.assertRaises(zipfile.LargeZipFile): zipf.writestr("foo%08d" % numfiles, b'') self.assertEqual(len(zipf.namelist()), numfiles) - with zipfile.ZipFile(TESTFN, mode="a", allowZip64=True) as zipf: + with zipfile2.ZipFile(TESTFN, mode="a", allowZip64=True) as zipf: zipf.debug = 100 self.assertEqual(len(zipf.namelist()), numfiles) numfiles2 = (1 << 16) * 3//2 @@ -133,7 +135,7 @@ def testMoreThan64kFilesAppend(self): zipf.writestr("foo%08d" % i, "%d" % (i**3 % 57)) self.assertEqual(len(zipf.namelist()), numfiles2) - with zipfile.ZipFile(TESTFN, mode="r") as zipf2: + with zipfile2.ZipFile(TESTFN, mode="r") as zipf2: self.assertEqual(len(zipf2.namelist()), numfiles2) for i in range(numfiles2): content = zipf2.read("foo%08d" % i).decode('ascii') diff --git a/cpython-tests/3.10/test_zipimport.py b/cpython-tests/3.10/test_zipimport.py index da66dad..adda5af 100644 --- a/cpython-tests/3.10/test_zipimport.py +++ b/cpython-tests/3.10/test_zipimport.py @@ -13,7 +13,7 @@ from test.support import import_helper from test.support import os_helper -from zipfile import ZipFile, ZipInfo, ZIP_STORED, ZIP_DEFLATED +from zipfile import ZipInfo, ZIP_STORED, ZIP_DEFLATED import zipimport import linecache @@ -26,6 +26,8 @@ except ImportError: zlib = None +from zipfile2 import ZipFile + test_src = """\ def get_name(): return __name__ diff --git a/cpython-tests/3.10/test_zipimport_support.py b/cpython-tests/3.10/test_zipimport_support.py deleted file mode 100644 index 7bf50a3..0000000 --- a/cpython-tests/3.10/test_zipimport_support.py +++ /dev/null @@ -1,243 +0,0 @@ -# This test module covers support in various parts of the standard library -# for working with modules located inside zipfiles -# The tests are centralised in this fashion to make it easy to drop them -# if a platform doesn't support zipimport -import test.support -import os -import os.path -import sys -import textwrap -import zipfile -import zipimport -import doctest -import inspect -import linecache -import unittest -from test.support import os_helper -from test.support.script_helper import (spawn_python, kill_python, assert_python_ok, - make_script, make_zip_script) - -verbose = test.support.verbose - -# Library modules covered by this test set -# pdb (Issue 4201) -# inspect (Issue 4223) -# doctest (Issue 4197) - -# Other test modules with zipimport related tests -# test_zipimport (of course!) -# test_cmd_line_script (covers the zipimport support in runpy) - -# Retrieve some helpers from other test cases -from test import (test_doctest, sample_doctest, sample_doctest_no_doctests, - sample_doctest_no_docstrings) - - -def _run_object_doctest(obj, module): - finder = doctest.DocTestFinder(verbose=verbose, recurse=False) - runner = doctest.DocTestRunner(verbose=verbose) - # Use the object's fully qualified name if it has one - # Otherwise, use the module's name - try: - name = "%s.%s" % (obj.__module__, obj.__qualname__) - except AttributeError: - name = module.__name__ - for example in finder.find(obj, name, module): - runner.run(example) - f, t = runner.failures, runner.tries - if f: - raise test.support.TestFailed("%d of %d doctests failed" % (f, t)) - if verbose: - print ('doctest (%s) ... %d tests with zero failures' % (module.__name__, t)) - return f, t - - - -class ZipSupportTests(unittest.TestCase): - # This used to use the ImportHooksBaseTestCase to restore - # the state of the import related information - # in the sys module after each test. However, that restores - # *too much* information and breaks for the invocation - # of test_doctest. So we do our own thing and leave - # sys.modules alone. - # We also clear the linecache and zipimport cache - # just to avoid any bogus errors due to name reuse in the tests - def setUp(self): - linecache.clearcache() - zipimport._zip_directory_cache.clear() - self.path = sys.path[:] - self.meta_path = sys.meta_path[:] - self.path_hooks = sys.path_hooks[:] - sys.path_importer_cache.clear() - - def tearDown(self): - sys.path[:] = self.path - sys.meta_path[:] = self.meta_path - sys.path_hooks[:] = self.path_hooks - sys.path_importer_cache.clear() - - def test_inspect_getsource_issue4223(self): - test_src = "def foo(): pass\n" - with os_helper.temp_dir() as d: - init_name = make_script(d, '__init__', test_src) - name_in_zip = os.path.join('zip_pkg', - os.path.basename(init_name)) - zip_name, run_name = make_zip_script(d, 'test_zip', - init_name, name_in_zip) - os.remove(init_name) - sys.path.insert(0, zip_name) - import zip_pkg - try: - self.assertEqual(inspect.getsource(zip_pkg.foo), test_src) - finally: - del sys.modules["zip_pkg"] - - def test_doctest_issue4197(self): - # To avoid having to keep two copies of the doctest module's - # unit tests in sync, this test works by taking the source of - # test_doctest itself, rewriting it a bit to cope with a new - # location, and then throwing it in a zip file to make sure - # everything still works correctly - test_src = inspect.getsource(test_doctest) - test_src = test_src.replace( - "from test import test_doctest", - "import test_zipped_doctest as test_doctest") - test_src = test_src.replace("test.test_doctest", - "test_zipped_doctest") - test_src = test_src.replace("test.sample_doctest", - "sample_zipped_doctest") - # The sample doctest files rewritten to include in the zipped version. - sample_sources = {} - for mod in [sample_doctest, sample_doctest_no_doctests, - sample_doctest_no_docstrings]: - src = inspect.getsource(mod) - src = src.replace("test.test_doctest", "test_zipped_doctest") - # Rewrite the module name so that, for example, - # "test.sample_doctest" becomes "sample_zipped_doctest". - mod_name = mod.__name__.split(".")[-1] - mod_name = mod_name.replace("sample_", "sample_zipped_") - sample_sources[mod_name] = src - - with os_helper.temp_dir() as d: - script_name = make_script(d, 'test_zipped_doctest', - test_src) - zip_name, run_name = make_zip_script(d, 'test_zip', - script_name) - with zipfile.ZipFile(zip_name, 'a') as z: - for mod_name, src in sample_sources.items(): - z.writestr(mod_name + ".py", src) - if verbose: - with zipfile.ZipFile(zip_name, 'r') as zip_file: - print ('Contents of %r:' % zip_name) - zip_file.printdir() - os.remove(script_name) - sys.path.insert(0, zip_name) - import test_zipped_doctest - try: - # Some of the doc tests depend on the colocated text files - # which aren't available to the zipped version (the doctest - # module currently requires real filenames for non-embedded - # tests). So we're forced to be selective about which tests - # to run. - # doctest could really use some APIs which take a text - # string or a file object instead of a filename... - known_good_tests = [ - test_zipped_doctest.SampleClass, - test_zipped_doctest.SampleClass.NestedClass, - test_zipped_doctest.SampleClass.NestedClass.__init__, - test_zipped_doctest.SampleClass.__init__, - test_zipped_doctest.SampleClass.a_classmethod, - test_zipped_doctest.SampleClass.a_property, - test_zipped_doctest.SampleClass.a_staticmethod, - test_zipped_doctest.SampleClass.double, - test_zipped_doctest.SampleClass.get, - test_zipped_doctest.SampleNewStyleClass, - test_zipped_doctest.SampleNewStyleClass.__init__, - test_zipped_doctest.SampleNewStyleClass.double, - test_zipped_doctest.SampleNewStyleClass.get, - test_zipped_doctest.sample_func, - test_zipped_doctest.test_DocTest, - test_zipped_doctest.test_DocTestParser, - test_zipped_doctest.test_DocTestRunner.basics, - test_zipped_doctest.test_DocTestRunner.exceptions, - test_zipped_doctest.test_DocTestRunner.option_directives, - test_zipped_doctest.test_DocTestRunner.optionflags, - test_zipped_doctest.test_DocTestRunner.verbose_flag, - test_zipped_doctest.test_Example, - test_zipped_doctest.test_debug, - test_zipped_doctest.test_testsource, - test_zipped_doctest.test_trailing_space_in_test, - test_zipped_doctest.test_DocTestSuite, - test_zipped_doctest.test_DocTestFinder, - ] - # These tests are the ones which need access - # to the data files, so we don't run them - fail_due_to_missing_data_files = [ - test_zipped_doctest.test_DocFileSuite, - test_zipped_doctest.test_testfile, - test_zipped_doctest.test_unittest_reportflags, - ] - - for obj in known_good_tests: - _run_object_doctest(obj, test_zipped_doctest) - finally: - del sys.modules["test_zipped_doctest"] - - def test_doctest_main_issue4197(self): - test_src = textwrap.dedent("""\ - class Test: - ">>> 'line 2'" - pass - - import doctest - doctest.testmod() - """) - pattern = 'File "%s", line 2, in %s' - with os_helper.temp_dir() as d: - script_name = make_script(d, 'script', test_src) - rc, out, err = assert_python_ok(script_name) - expected = pattern % (script_name, "__main__.Test") - if verbose: - print ("Expected line", expected) - print ("Got stdout:") - print (ascii(out)) - self.assertIn(expected.encode('utf-8'), out) - zip_name, run_name = make_zip_script(d, "test_zip", - script_name, '__main__.py') - rc, out, err = assert_python_ok(zip_name) - expected = pattern % (run_name, "__main__.Test") - if verbose: - print ("Expected line", expected) - print ("Got stdout:") - print (ascii(out)) - self.assertIn(expected.encode('utf-8'), out) - - def test_pdb_issue4201(self): - test_src = textwrap.dedent("""\ - def f(): - pass - - import pdb - pdb.Pdb(nosigint=True).runcall(f) - """) - with os_helper.temp_dir() as d: - script_name = make_script(d, 'script', test_src) - p = spawn_python(script_name) - p.stdin.write(b'l\n') - data = kill_python(p) - # bdb/pdb applies normcase to its filename before displaying - self.assertIn(os.path.normcase(script_name.encode('utf-8')), data) - zip_name, run_name = make_zip_script(d, "test_zip", - script_name, '__main__.py') - p = spawn_python(zip_name) - p.stdin.write(b'l\n') - data = kill_python(p) - # bdb/pdb applies normcase to its filename before displaying - self.assertIn(os.path.normcase(run_name.encode('utf-8')), data) - - -def tearDownModule(): - test.support.reap_children() - -if __name__ == '__main__': - unittest.main() diff --git a/cpython-tests/3.11/test_zipfile64.py b/cpython-tests/3.11/test_zipfile64.py index 0947013..8b511f8 100644 --- a/cpython-tests/3.11/test_zipfile64.py +++ b/cpython-tests/3.11/test_zipfile64.py @@ -20,6 +20,8 @@ from test.support import os_helper from test.support import requires_zlib +import zipfile2 + TESTFN = os_helper.TESTFN TESTFN2 = TESTFN + "2" @@ -38,7 +40,7 @@ def setUp(self): def zipTest(self, f, compression): # Create the ZIP archive. - with zipfile.ZipFile(f, "w", compression) as zipfp: + with zipfile2.ZipFile(f, "w", compression) as zipfp: # It will contain enough copies of self.data to reach about 6 GiB of # raw data to store. @@ -56,7 +58,7 @@ def zipTest(self, f, compression): sys.__stdout__.flush() # Read the ZIP archive - with zipfile.ZipFile(f, "r", compression) as zipfp: + with zipfile2.ZipFile(f, "r", compression) as zipfp: for num in range(filecount): self.assertEqual(zipfp.read("testfn%d" % num), self.data) # Print still working message since this test can be really slow @@ -94,21 +96,21 @@ class OtherTests(unittest.TestCase): def testMoreThan64kFiles(self): # This test checks that more than 64k files can be added to an archive, # and that the resulting archive can be read properly by ZipFile - with zipfile.ZipFile(TESTFN, mode="w", allowZip64=True) as zipf: + with zipfile2.ZipFile(TESTFN, mode="w", allowZip64=True) as zipf: zipf.debug = 100 numfiles = (1 << 16) * 3//2 for i in range(numfiles): zipf.writestr("foo%08d" % i, "%d" % (i**3 % 57)) self.assertEqual(len(zipf.namelist()), numfiles) - with zipfile.ZipFile(TESTFN, mode="r") as zipf2: + with zipfile2.ZipFile(TESTFN, mode="r") as zipf2: self.assertEqual(len(zipf2.namelist()), numfiles) for i in range(numfiles): content = zipf2.read("foo%08d" % i).decode('ascii') self.assertEqual(content, "%d" % (i**3 % 57)) def testMoreThan64kFilesAppend(self): - with zipfile.ZipFile(TESTFN, mode="w", allowZip64=False) as zipf: + with zipfile2.ZipFile(TESTFN, mode="w", allowZip64=False) as zipf: zipf.debug = 100 numfiles = (1 << 16) - 1 for i in range(numfiles): @@ -118,14 +120,14 @@ def testMoreThan64kFilesAppend(self): zipf.writestr("foo%08d" % numfiles, b'') self.assertEqual(len(zipf.namelist()), numfiles) - with zipfile.ZipFile(TESTFN, mode="a", allowZip64=False) as zipf: + with zipfile2.ZipFile(TESTFN, mode="a", allowZip64=False) as zipf: zipf.debug = 100 self.assertEqual(len(zipf.namelist()), numfiles) with self.assertRaises(zipfile.LargeZipFile): zipf.writestr("foo%08d" % numfiles, b'') self.assertEqual(len(zipf.namelist()), numfiles) - with zipfile.ZipFile(TESTFN, mode="a", allowZip64=True) as zipf: + with zipfile2.ZipFile(TESTFN, mode="a", allowZip64=True) as zipf: zipf.debug = 100 self.assertEqual(len(zipf.namelist()), numfiles) numfiles2 = (1 << 16) * 3//2 @@ -133,7 +135,7 @@ def testMoreThan64kFilesAppend(self): zipf.writestr("foo%08d" % i, "%d" % (i**3 % 57)) self.assertEqual(len(zipf.namelist()), numfiles2) - with zipfile.ZipFile(TESTFN, mode="r") as zipf2: + with zipfile2.ZipFile(TESTFN, mode="r") as zipf2: self.assertEqual(len(zipf2.namelist()), numfiles2) for i in range(numfiles2): content = zipf2.read("foo%08d" % i).decode('ascii') diff --git a/cpython-tests/3.11/test_zipimport.py b/cpython-tests/3.11/test_zipimport.py index 59a5200..ad65685 100644 --- a/cpython-tests/3.11/test_zipimport.py +++ b/cpython-tests/3.11/test_zipimport.py @@ -13,7 +13,7 @@ from test.support import import_helper from test.support import os_helper -from zipfile import ZipFile, ZipInfo, ZIP_STORED, ZIP_DEFLATED +from zipfile import ZipInfo, ZIP_STORED, ZIP_DEFLATED import zipimport import linecache @@ -26,6 +26,8 @@ except ImportError: zlib = None +from zipfile2 import ZipFile + test_src = """\ def get_name(): return __name__ diff --git a/cpython-tests/3.12/test_zipfile64.py b/cpython-tests/3.12/test_zipfile64.py index 2e1affe..bc9b607 100644 --- a/cpython-tests/3.12/test_zipfile64.py +++ b/cpython-tests/3.12/test_zipfile64.py @@ -20,6 +20,8 @@ from test.support import os_helper from test.support import requires_zlib +import zipfile2 + TESTFN = os_helper.TESTFN TESTFN2 = TESTFN + "2" @@ -34,7 +36,7 @@ def setUp(self): def zipTest(self, f, compression): # Create the ZIP archive. - with zipfile.ZipFile(f, "w", compression) as zipfp: + with zipfile2.ZipFile(f, "w", compression) as zipfp: # It will contain enough copies of self.data to reach about 6 GiB of # raw data to store. @@ -52,7 +54,7 @@ def zipTest(self, f, compression): sys.__stdout__.flush() # Read the ZIP archive - with zipfile.ZipFile(f, "r", compression) as zipfp: + with zipfile2.ZipFile(f, "r", compression) as zipfp: for num in range(filecount): self.assertEqual(zipfp.read("testfn%d" % num), self.data) # Print still working message since this test can be really slow @@ -91,21 +93,21 @@ class OtherTests(unittest.TestCase): def testMoreThan64kFiles(self): # This test checks that more than 64k files can be added to an archive, # and that the resulting archive can be read properly by ZipFile - with zipfile.ZipFile(TESTFN, mode="w", allowZip64=True) as zipf: + with zipfile2.ZipFile(TESTFN, mode="w", allowZip64=True) as zipf: zipf.debug = 100 numfiles = (1 << 16) * 3//2 for i in range(numfiles): zipf.writestr("foo%08d" % i, "%d" % (i**3 % 57)) self.assertEqual(len(zipf.namelist()), numfiles) - with zipfile.ZipFile(TESTFN, mode="r") as zipf2: + with zipfile2.ZipFile(TESTFN, mode="r") as zipf2: self.assertEqual(len(zipf2.namelist()), numfiles) for i in range(numfiles): content = zipf2.read("foo%08d" % i).decode('ascii') self.assertEqual(content, "%d" % (i**3 % 57)) def testMoreThan64kFilesAppend(self): - with zipfile.ZipFile(TESTFN, mode="w", allowZip64=False) as zipf: + with zipfile2.ZipFile(TESTFN, mode="w", allowZip64=False) as zipf: zipf.debug = 100 numfiles = (1 << 16) - 1 for i in range(numfiles): @@ -115,14 +117,14 @@ def testMoreThan64kFilesAppend(self): zipf.writestr("foo%08d" % numfiles, b'') self.assertEqual(len(zipf.namelist()), numfiles) - with zipfile.ZipFile(TESTFN, mode="a", allowZip64=False) as zipf: + with zipfile2.ZipFile(TESTFN, mode="a", allowZip64=False) as zipf: zipf.debug = 100 self.assertEqual(len(zipf.namelist()), numfiles) with self.assertRaises(zipfile.LargeZipFile): zipf.writestr("foo%08d" % numfiles, b'') self.assertEqual(len(zipf.namelist()), numfiles) - with zipfile.ZipFile(TESTFN, mode="a", allowZip64=True) as zipf: + with zipfile2.ZipFile(TESTFN, mode="a", allowZip64=True) as zipf: zipf.debug = 100 self.assertEqual(len(zipf.namelist()), numfiles) numfiles2 = (1 << 16) * 3//2 @@ -130,7 +132,7 @@ def testMoreThan64kFilesAppend(self): zipf.writestr("foo%08d" % i, "%d" % (i**3 % 57)) self.assertEqual(len(zipf.namelist()), numfiles2) - with zipfile.ZipFile(TESTFN, mode="r") as zipf2: + with zipfile2.ZipFile(TESTFN, mode="r") as zipf2: self.assertEqual(len(zipf2.namelist()), numfiles2) for i in range(numfiles2): content = zipf2.read("foo%08d" % i).decode('ascii') diff --git a/cpython-tests/3.12/test_zipimport.py b/cpython-tests/3.12/test_zipimport.py index 12fd982..8a9e665 100644 --- a/cpython-tests/3.12/test_zipimport.py +++ b/cpython-tests/3.12/test_zipimport.py @@ -13,7 +13,7 @@ from test.support import import_helper from test.support import os_helper -from zipfile import ZipFile, ZipInfo, ZIP_STORED, ZIP_DEFLATED +from zipfile import ZipInfo, ZIP_STORED, ZIP_DEFLATED import zipimport import linecache @@ -26,6 +26,9 @@ except ImportError: zlib = None +from zipfile2 import ZipFile + + test_src = """\ def get_name(): return __name__ diff --git a/cpython-tests/3.12/test_zipimport_support.py b/cpython-tests/3.12/test_zipimport_support.py deleted file mode 100644 index 71039d2..0000000 --- a/cpython-tests/3.12/test_zipimport_support.py +++ /dev/null @@ -1,244 +0,0 @@ -# This test module covers support in various parts of the standard library -# for working with modules located inside zipfiles -# The tests are centralised in this fashion to make it easy to drop them -# if a platform doesn't support zipimport -import test.support -import os -import os.path -import sys -import textwrap -import zipfile -import zipimport -import doctest -import inspect -import linecache -import unittest -from test.support import os_helper -from test.support.script_helper import (spawn_python, kill_python, assert_python_ok, - make_script, make_zip_script) - -verbose = test.support.verbose - -# Library modules covered by this test set -# pdb (Issue 4201) -# inspect (Issue 4223) -# doctest (Issue 4197) - -# Other test modules with zipimport related tests -# test_zipimport (of course!) -# test_cmd_line_script (covers the zipimport support in runpy) - -# Retrieve some helpers from other test cases -from test.test_doctest import (test_doctest, - sample_doctest, sample_doctest_no_doctests, - sample_doctest_no_docstrings) - - -def _run_object_doctest(obj, module): - finder = doctest.DocTestFinder(verbose=verbose, recurse=False) - runner = doctest.DocTestRunner(verbose=verbose) - # Use the object's fully qualified name if it has one - # Otherwise, use the module's name - try: - name = "%s.%s" % (obj.__module__, obj.__qualname__) - except AttributeError: - name = module.__name__ - for example in finder.find(obj, name, module): - runner.run(example) - f, t = runner.failures, runner.tries - if f: - raise test.support.TestFailed("%d of %d doctests failed" % (f, t)) - if verbose: - print ('doctest (%s) ... %d tests with zero failures' % (module.__name__, t)) - return f, t - - - -class ZipSupportTests(unittest.TestCase): - # This used to use the ImportHooksBaseTestCase to restore - # the state of the import related information - # in the sys module after each test. However, that restores - # *too much* information and breaks for the invocation - # of test_doctest. So we do our own thing and leave - # sys.modules alone. - # We also clear the linecache and zipimport cache - # just to avoid any bogus errors due to name reuse in the tests - def setUp(self): - linecache.clearcache() - zipimport._zip_directory_cache.clear() - self.path = sys.path[:] - self.meta_path = sys.meta_path[:] - self.path_hooks = sys.path_hooks[:] - sys.path_importer_cache.clear() - - def tearDown(self): - sys.path[:] = self.path - sys.meta_path[:] = self.meta_path - sys.path_hooks[:] = self.path_hooks - sys.path_importer_cache.clear() - - def test_inspect_getsource_issue4223(self): - test_src = "def foo(): pass\n" - with os_helper.temp_dir() as d: - init_name = make_script(d, '__init__', test_src) - name_in_zip = os.path.join('zip_pkg', - os.path.basename(init_name)) - zip_name, run_name = make_zip_script(d, 'test_zip', - init_name, name_in_zip) - os.remove(init_name) - sys.path.insert(0, zip_name) - import zip_pkg - try: - self.assertEqual(inspect.getsource(zip_pkg.foo), test_src) - finally: - del sys.modules["zip_pkg"] - - def test_doctest_issue4197(self): - # To avoid having to keep two copies of the doctest module's - # unit tests in sync, this test works by taking the source of - # test_doctest itself, rewriting it a bit to cope with a new - # location, and then throwing it in a zip file to make sure - # everything still works correctly - test_src = inspect.getsource(test_doctest) - test_src = test_src.replace( - "from test.test_doctest import test_doctest", - "import test_zipped_doctest as test_doctest") - test_src = test_src.replace("test.test_doctest.test_doctest", - "test_zipped_doctest") - test_src = test_src.replace("test.test_doctest.sample_doctest", - "sample_zipped_doctest") - # The sample doctest files rewritten to include in the zipped version. - sample_sources = {} - for mod in [sample_doctest, sample_doctest_no_doctests, - sample_doctest_no_docstrings]: - src = inspect.getsource(mod) - src = src.replace("test.test_doctest.test_doctest", "test_zipped_doctest") - # Rewrite the module name so that, for example, - # "test.sample_doctest" becomes "sample_zipped_doctest". - mod_name = mod.__name__.split(".")[-1] - mod_name = mod_name.replace("sample_", "sample_zipped_") - sample_sources[mod_name] = src - - with os_helper.temp_dir() as d: - script_name = make_script(d, 'test_zipped_doctest', - test_src) - zip_name, run_name = make_zip_script(d, 'test_zip', - script_name) - with zipfile.ZipFile(zip_name, 'a') as z: - for mod_name, src in sample_sources.items(): - z.writestr(mod_name + ".py", src) - if verbose: - with zipfile.ZipFile(zip_name, 'r') as zip_file: - print ('Contents of %r:' % zip_name) - zip_file.printdir() - os.remove(script_name) - sys.path.insert(0, zip_name) - import test_zipped_doctest - try: - # Some of the doc tests depend on the colocated text files - # which aren't available to the zipped version (the doctest - # module currently requires real filenames for non-embedded - # tests). So we're forced to be selective about which tests - # to run. - # doctest could really use some APIs which take a text - # string or a file object instead of a filename... - known_good_tests = [ - test_zipped_doctest.SampleClass, - test_zipped_doctest.SampleClass.NestedClass, - test_zipped_doctest.SampleClass.NestedClass.__init__, - test_zipped_doctest.SampleClass.__init__, - test_zipped_doctest.SampleClass.a_classmethod, - test_zipped_doctest.SampleClass.a_property, - test_zipped_doctest.SampleClass.a_staticmethod, - test_zipped_doctest.SampleClass.double, - test_zipped_doctest.SampleClass.get, - test_zipped_doctest.SampleNewStyleClass, - test_zipped_doctest.SampleNewStyleClass.__init__, - test_zipped_doctest.SampleNewStyleClass.double, - test_zipped_doctest.SampleNewStyleClass.get, - test_zipped_doctest.sample_func, - test_zipped_doctest.test_DocTest, - test_zipped_doctest.test_DocTestParser, - test_zipped_doctest.test_DocTestRunner.basics, - test_zipped_doctest.test_DocTestRunner.exceptions, - test_zipped_doctest.test_DocTestRunner.option_directives, - test_zipped_doctest.test_DocTestRunner.optionflags, - test_zipped_doctest.test_DocTestRunner.verbose_flag, - test_zipped_doctest.test_Example, - test_zipped_doctest.test_debug, - test_zipped_doctest.test_testsource, - test_zipped_doctest.test_trailing_space_in_test, - test_zipped_doctest.test_DocTestSuite, - test_zipped_doctest.test_DocTestFinder, - ] - # These tests are the ones which need access - # to the data files, so we don't run them - fail_due_to_missing_data_files = [ - test_zipped_doctest.test_DocFileSuite, - test_zipped_doctest.test_testfile, - test_zipped_doctest.test_unittest_reportflags, - ] - - for obj in known_good_tests: - _run_object_doctest(obj, test_zipped_doctest) - finally: - del sys.modules["test_zipped_doctest"] - - def test_doctest_main_issue4197(self): - test_src = textwrap.dedent("""\ - class Test: - ">>> 'line 2'" - pass - - import doctest - doctest.testmod() - """) - pattern = 'File "%s", line 2, in %s' - with os_helper.temp_dir() as d: - script_name = make_script(d, 'script', test_src) - rc, out, err = assert_python_ok(script_name) - expected = pattern % (script_name, "__main__.Test") - if verbose: - print ("Expected line", expected) - print ("Got stdout:") - print (ascii(out)) - self.assertIn(expected.encode('utf-8'), out) - zip_name, run_name = make_zip_script(d, "test_zip", - script_name, '__main__.py') - rc, out, err = assert_python_ok(zip_name) - expected = pattern % (run_name, "__main__.Test") - if verbose: - print ("Expected line", expected) - print ("Got stdout:") - print (ascii(out)) - self.assertIn(expected.encode('utf-8'), out) - - def test_pdb_issue4201(self): - test_src = textwrap.dedent("""\ - def f(): - pass - - import pdb - pdb.Pdb(nosigint=True).runcall(f) - """) - with os_helper.temp_dir() as d: - script_name = make_script(d, 'script', test_src) - p = spawn_python(script_name) - p.stdin.write(b'l\n') - data = kill_python(p) - # bdb/pdb applies normcase to its filename before displaying - self.assertIn(os.path.normcase(script_name.encode('utf-8')), data) - zip_name, run_name = make_zip_script(d, "test_zip", - script_name, '__main__.py') - p = spawn_python(zip_name) - p.stdin.write(b'l\n') - data = kill_python(p) - # bdb/pdb applies normcase to its filename before displaying - self.assertIn(os.path.normcase(run_name.encode('utf-8')), data) - - -def tearDownModule(): - test.support.reap_children() - -if __name__ == '__main__': - unittest.main() diff --git a/cpython-tests/3.8/test_zipfile.py b/cpython-tests/3.8/test_zipfile.py index d245305..60574e0 100644 --- a/cpython-tests/3.8/test_zipfile.py +++ b/cpython-tests/3.8/test_zipfile.py @@ -23,6 +23,8 @@ requires_zlib, requires_bz2, requires_lzma, captured_stdout) +import zipfile2 + TESTFN2 = TESTFN + "2" TESTFNDIR = TESTFN + "d" FIXEDTEST_SIZE = 1000 @@ -61,7 +63,7 @@ def setUp(self): def make_test_archive(self, f, compression, compresslevel=None): kwargs = {'compression': compression, 'compresslevel': compresslevel} # Create the ZIP archive - with zipfile.ZipFile(f, "w", **kwargs) as zipfp: + with zipfile2.ZipFile(f, "w", **kwargs) as zipfp: zipfp.write(TESTFN, "another.name") zipfp.write(TESTFN, TESTFN) zipfp.writestr("strfile", self.data) @@ -73,7 +75,7 @@ def zip_test(self, f, compression, compresslevel=None): self.make_test_archive(f, compression, compresslevel) # Read the ZIP archive - with zipfile.ZipFile(f, "r", compression) as zipfp: + with zipfile2.ZipFile(f, "r", compression) as zipfp: self.assertEqual(zipfp.read(TESTFN), self.data) self.assertEqual(zipfp.read("another.name"), self.data) self.assertEqual(zipfp.read("strfile"), self.data) @@ -131,7 +133,7 @@ def zip_open_test(self, f, compression): self.make_test_archive(f, compression) # Read the ZIP archive - with zipfile.ZipFile(f, "r", compression) as zipfp: + with zipfile2.ZipFile(f, "r", compression) as zipfp: zipdata1 = [] with zipfp.open(TESTFN) as zipopen1: while True: @@ -158,14 +160,14 @@ def test_open(self): def test_open_with_pathlike(self): path = pathlib.Path(TESTFN2) self.zip_open_test(path, self.compression) - with zipfile.ZipFile(path, "r", self.compression) as zipfp: + with zipfile2.ZipFile(path, "r", self.compression) as zipfp: self.assertIsInstance(zipfp.filename, str) def zip_random_open_test(self, f, compression): self.make_test_archive(f, compression) # Read the ZIP archive - with zipfile.ZipFile(f, "r", compression) as zipfp: + with zipfile2.ZipFile(f, "r", compression) as zipfp: zipdata1 = [] with zipfp.open(TESTFN) as zipopen1: while True: @@ -184,7 +186,7 @@ def zip_read1_test(self, f, compression): self.make_test_archive(f, compression) # Read the ZIP archive - with zipfile.ZipFile(f, "r") as zipfp, \ + with zipfile2.ZipFile(f, "r") as zipfp, \ zipfp.open(TESTFN) as zipopen: zipdata = [] while True: @@ -203,7 +205,7 @@ def zip_read1_10_test(self, f, compression): self.make_test_archive(f, compression) # Read the ZIP archive - with zipfile.ZipFile(f, "r") as zipfp, \ + with zipfile2.ZipFile(f, "r") as zipfp, \ zipfp.open(TESTFN) as zipopen: zipdata = [] while True: @@ -223,7 +225,7 @@ def zip_readline_read_test(self, f, compression): self.make_test_archive(f, compression) # Read the ZIP archive - with zipfile.ZipFile(f, "r") as zipfp, \ + with zipfile2.ZipFile(f, "r") as zipfp, \ zipfp.open(TESTFN) as zipopen: data = b'' while True: @@ -248,7 +250,7 @@ def zip_readline_test(self, f, compression): self.make_test_archive(f, compression) # Read the ZIP archive - with zipfile.ZipFile(f, "r") as zipfp: + with zipfile2.ZipFile(f, "r") as zipfp: with zipfp.open(TESTFN) as zipopen: for line in self.line_gen: linedata = zipopen.readline() @@ -262,7 +264,7 @@ def zip_readlines_test(self, f, compression): self.make_test_archive(f, compression) # Read the ZIP archive - with zipfile.ZipFile(f, "r") as zipfp: + with zipfile2.ZipFile(f, "r") as zipfp: with zipfp.open(TESTFN) as zipopen: ziplines = zipopen.readlines() for line, zipline in zip(self.line_gen, ziplines): @@ -276,7 +278,7 @@ def zip_iterlines_test(self, f, compression): self.make_test_archive(f, compression) # Read the ZIP archive - with zipfile.ZipFile(f, "r") as zipfp: + with zipfile2.ZipFile(f, "r") as zipfp: with zipfp.open(TESTFN) as zipopen: for line, zipline in zip(self.line_gen, zipopen): self.assertEqual(zipline, line) @@ -288,23 +290,23 @@ def test_iterlines(self): def test_low_compression(self): """Check for cases where compressed data is larger than original.""" # Create the ZIP archive - with zipfile.ZipFile(TESTFN2, "w", self.compression) as zipfp: + with zipfile2.ZipFile(TESTFN2, "w", self.compression) as zipfp: zipfp.writestr("strfile", '12') # Get an open object for strfile - with zipfile.ZipFile(TESTFN2, "r", self.compression) as zipfp: + with zipfile2.ZipFile(TESTFN2, "r", self.compression) as zipfp: with zipfp.open("strfile") as openobj: self.assertEqual(openobj.read(1), b'1') self.assertEqual(openobj.read(1), b'2') def test_writestr_compression(self): - zipfp = zipfile.ZipFile(TESTFN2, "w") + zipfp = zipfile2.ZipFile(TESTFN2, "w") zipfp.writestr("b.txt", "hello world", compress_type=self.compression) info = zipfp.getinfo('b.txt') self.assertEqual(info.compress_type, self.compression) def test_writestr_compresslevel(self): - zipfp = zipfile.ZipFile(TESTFN2, "w", compresslevel=1) + zipfp = zipfile2.ZipFile(TESTFN2, "w", compresslevel=1) zipfp.writestr("a.txt", "hello world", compress_type=self.compression) zipfp.writestr("b.txt", "hello world", compress_type=self.compression, compresslevel=2) @@ -325,7 +327,7 @@ def test_read_return_size(self): for test_size in (1, 4095, 4096, 4097, 16384): file_size = test_size + 1 junk = getrandbytes(file_size) - with zipfile.ZipFile(io.BytesIO(), "w", self.compression) as zipf: + with zipfile2.ZipFile(io.BytesIO(), "w", self.compression) as zipf: zipf.writestr('foo', junk) with zipf.open('foo', 'r') as fp: buf = fp.read(test_size) @@ -333,20 +335,20 @@ def test_read_return_size(self): def test_truncated_zipfile(self): fp = io.BytesIO() - with zipfile.ZipFile(fp, mode='w') as zipf: + with zipfile2.ZipFile(fp, mode='w') as zipf: zipf.writestr('strfile', self.data, compress_type=self.compression) end_offset = fp.tell() zipfiledata = fp.getvalue() fp = io.BytesIO(zipfiledata) - with zipfile.ZipFile(fp) as zipf: + with zipfile2.ZipFile(fp) as zipf: with zipf.open('strfile') as zipopen: fp.truncate(end_offset - 20) with self.assertRaises(EOFError): zipopen.read() fp = io.BytesIO(zipfiledata) - with zipfile.ZipFile(fp) as zipf: + with zipfile2.ZipFile(fp) as zipf: with zipf.open('strfile') as zipopen: fp.truncate(end_offset - 20) with self.assertRaises(EOFError): @@ -354,7 +356,7 @@ def test_truncated_zipfile(self): pass fp = io.BytesIO(zipfiledata) - with zipfile.ZipFile(fp) as zipf: + with zipfile2.ZipFile(fp) as zipf: with zipf.open('strfile') as zipopen: fp.truncate(end_offset - 20) with self.assertRaises(EOFError): @@ -364,12 +366,12 @@ def test_truncated_zipfile(self): def test_repr(self): fname = 'file.name' for f in get_files(self): - with zipfile.ZipFile(f, 'w', self.compression) as zipfp: + with zipfile2.ZipFile(f, 'w', self.compression) as zipfp: zipfp.write(TESTFN, fname) r = repr(zipfp) self.assertIn("mode='w'", r) - with zipfile.ZipFile(f, 'r') as zipfp: + with zipfile2.ZipFile(f, 'r') as zipfp: r = repr(zipfp) if isinstance(f, str): self.assertIn('filename=%r' % f, r) @@ -399,7 +401,7 @@ def test_compresslevel_basic(self): def test_per_file_compresslevel(self): """Check that files within a Zip archive can have different compression levels.""" - with zipfile.ZipFile(TESTFN2, "w", compresslevel=1) as zipfp: + with zipfile2.ZipFile(TESTFN2, "w", compresslevel=1) as zipfp: zipfp.write(TESTFN, 'compress_1') zipfp.write(TESTFN, 'compress_9', compresslevel=9) one_info = zipfp.getinfo('compress_1') @@ -421,7 +423,7 @@ def write(self, data): while True: testfile = BrokenFile() count = None - with zipfile.ZipFile(testfile, 'w', self.compression) as zipfp: + with zipfile2.ZipFile(testfile, 'w', self.compression) as zipfp: with zipfp.open('file1', 'w') as f: f.write(b'data1') count = 0 @@ -434,11 +436,11 @@ def write(self, data): break finally: count = None - with zipfile.ZipFile(io.BytesIO(testfile.getvalue())) as zipfp: + with zipfile2.ZipFile(io.BytesIO(testfile.getvalue())) as zipfp: self.assertEqual(zipfp.namelist(), ['file1']) self.assertEqual(zipfp.read('file1'), b'data1') - with zipfile.ZipFile(io.BytesIO(testfile.getvalue())) as zipfp: + with zipfile2.ZipFile(io.BytesIO(testfile.getvalue())) as zipfp: self.assertEqual(zipfp.namelist(), ['file1', 'file2']) self.assertEqual(zipfp.read('file1'), b'data1') self.assertEqual(zipfp.read('file2'), b'data2') @@ -460,7 +462,7 @@ def zip_test_writestr_permissions(self, f, compression): # instance. self.make_test_archive(f, compression) - with zipfile.ZipFile(f, "r") as zipfp: + with zipfile2.ZipFile(f, "r") as zipfp: zinfo = zipfp.getinfo('strfile') self.assertEqual(zinfo.external_attr, 0o600 << 16) @@ -472,18 +474,18 @@ def test_writestr_permissions(self): self.zip_test_writestr_permissions(f, zipfile.ZIP_STORED) def test_absolute_arcnames(self): - with zipfile.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED) as zipfp: + with zipfile2.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED) as zipfp: zipfp.write(TESTFN, "/absolute") - with zipfile.ZipFile(TESTFN2, "r", zipfile.ZIP_STORED) as zipfp: + with zipfile2.ZipFile(TESTFN2, "r", zipfile.ZIP_STORED) as zipfp: self.assertEqual(zipfp.namelist(), ["absolute"]) def test_append_to_zip_file(self): """Test appending to an existing zipfile.""" - with zipfile.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED) as zipfp: + with zipfile2.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED) as zipfp: zipfp.write(TESTFN, TESTFN) - with zipfile.ZipFile(TESTFN2, "a", zipfile.ZIP_STORED) as zipfp: + with zipfile2.ZipFile(TESTFN2, "a", zipfile.ZIP_STORED) as zipfp: zipfp.writestr("strfile", self.data) self.assertEqual(zipfp.namelist(), [TESTFN, "strfile"]) @@ -495,24 +497,24 @@ def test_append_to_non_zip_file(self): with open(TESTFN2, 'wb') as f: f.write(data) - with zipfile.ZipFile(TESTFN2, "a", zipfile.ZIP_STORED) as zipfp: + with zipfile2.ZipFile(TESTFN2, "a", zipfile.ZIP_STORED) as zipfp: zipfp.write(TESTFN, TESTFN) with open(TESTFN2, 'rb') as f: f.seek(len(data)) - with zipfile.ZipFile(f, "r") as zipfp: + with zipfile2.ZipFile(f, "r") as zipfp: self.assertEqual(zipfp.namelist(), [TESTFN]) self.assertEqual(zipfp.read(TESTFN), self.data) with open(TESTFN2, 'rb') as f: self.assertEqual(f.read(len(data)), data) zipfiledata = f.read() - with io.BytesIO(zipfiledata) as bio, zipfile.ZipFile(bio) as zipfp: + with io.BytesIO(zipfiledata) as bio, zipfile2.ZipFile(bio) as zipfp: self.assertEqual(zipfp.namelist(), [TESTFN]) self.assertEqual(zipfp.read(TESTFN), self.data) def test_read_concatenated_zip_file(self): with io.BytesIO() as bio: - with zipfile.ZipFile(bio, 'w', zipfile.ZIP_STORED) as zipfp: + with zipfile2.ZipFile(bio, 'w', zipfile.ZIP_STORED) as zipfp: zipfp.write(TESTFN, TESTFN) zipfiledata = bio.getvalue() data = b'I am not a ZipFile!'*10 @@ -520,13 +522,13 @@ def test_read_concatenated_zip_file(self): f.write(data) f.write(zipfiledata) - with zipfile.ZipFile(TESTFN2) as zipfp: + with zipfile2.ZipFile(TESTFN2) as zipfp: self.assertEqual(zipfp.namelist(), [TESTFN]) self.assertEqual(zipfp.read(TESTFN), self.data) def test_append_to_concatenated_zip_file(self): with io.BytesIO() as bio: - with zipfile.ZipFile(bio, 'w', zipfile.ZIP_STORED) as zipfp: + with zipfile2.ZipFile(bio, 'w', zipfile.ZIP_STORED) as zipfp: zipfp.write(TESTFN, TESTFN) zipfiledata = bio.getvalue() data = b'I am not a ZipFile!'*1000000 @@ -534,40 +536,40 @@ def test_append_to_concatenated_zip_file(self): f.write(data) f.write(zipfiledata) - with zipfile.ZipFile(TESTFN2, 'a') as zipfp: + with zipfile2.ZipFile(TESTFN2, 'a') as zipfp: self.assertEqual(zipfp.namelist(), [TESTFN]) zipfp.writestr('strfile', self.data) with open(TESTFN2, 'rb') as f: self.assertEqual(f.read(len(data)), data) zipfiledata = f.read() - with io.BytesIO(zipfiledata) as bio, zipfile.ZipFile(bio) as zipfp: + with io.BytesIO(zipfiledata) as bio, zipfile2.ZipFile(bio) as zipfp: self.assertEqual(zipfp.namelist(), [TESTFN, 'strfile']) self.assertEqual(zipfp.read(TESTFN), self.data) self.assertEqual(zipfp.read('strfile'), self.data) def test_ignores_newline_at_end(self): - with zipfile.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED) as zipfp: + with zipfile2.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED) as zipfp: zipfp.write(TESTFN, TESTFN) with open(TESTFN2, 'a') as f: f.write("\r\n\00\00\00") - with zipfile.ZipFile(TESTFN2, "r") as zipfp: - self.assertIsInstance(zipfp, zipfile.ZipFile) + with zipfile2.ZipFile(TESTFN2, "r") as zipfp: + self.assertIsInstance(zipfp, zipfile2.ZipFile) def test_ignores_stuff_appended_past_comments(self): - with zipfile.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED) as zipfp: + with zipfile2.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED) as zipfp: zipfp.comment = b"this is a comment" zipfp.write(TESTFN, TESTFN) with open(TESTFN2, 'a') as f: f.write("abcdef\r\n") - with zipfile.ZipFile(TESTFN2, "r") as zipfp: - self.assertIsInstance(zipfp, zipfile.ZipFile) + with zipfile2.ZipFile(TESTFN2, "r") as zipfp: + self.assertIsInstance(zipfp, zipfile2.ZipFile) self.assertEqual(zipfp.comment, b"this is a comment") def test_write_default_name(self): """Check that calling ZipFile.write without arcname specified produces the expected result.""" - with zipfile.ZipFile(TESTFN2, "w") as zipfp: + with zipfile2.ZipFile(TESTFN2, "w") as zipfp: zipfp.write(TESTFN) with open(TESTFN, "rb") as f: self.assertEqual(zipfp.read(TESTFN), f.read()) @@ -575,23 +577,23 @@ def test_write_default_name(self): def test_write_to_readonly(self): """Check that trying to call write() on a readonly ZipFile object raises a ValueError.""" - with zipfile.ZipFile(TESTFN2, mode="w") as zipfp: + with zipfile2.ZipFile(TESTFN2, mode="w") as zipfp: zipfp.writestr("somefile.txt", "bogus") - with zipfile.ZipFile(TESTFN2, mode="r") as zipfp: + with zipfile2.ZipFile(TESTFN2, mode="r") as zipfp: self.assertRaises(ValueError, zipfp.write, TESTFN) - with zipfile.ZipFile(TESTFN2, mode="r") as zipfp: + with zipfile2.ZipFile(TESTFN2, mode="r") as zipfp: with self.assertRaises(ValueError): zipfp.open(TESTFN, mode='w') def test_add_file_before_1980(self): # Set atime and mtime to 1970-01-01 os.utime(TESTFN, (0, 0)) - with zipfile.ZipFile(TESTFN2, "w") as zipfp: + with zipfile2.ZipFile(TESTFN2, "w") as zipfp: self.assertRaises(ValueError, zipfp.write, TESTFN) - with zipfile.ZipFile(TESTFN2, "w", strict_timestamps=False) as zipfp: + with zipfile2.ZipFile(TESTFN2, "w", strict_timestamps=False) as zipfp: zipfp.write(TESTFN) zinfo = zipfp.getinfo(TESTFN) self.assertEqual(zinfo.date_time, (1980, 1, 1, 0, 0, 0)) @@ -620,10 +622,10 @@ def test_add_file_after_2107(self): # https://bugs.python.org/issue39460#msg360952 self.skipTest(f"Linux VFS/XFS kernel bug detected: {mtime_ns=}") - with zipfile.ZipFile(TESTFN2, "w") as zipfp: + with zipfile2.ZipFile(TESTFN2, "w") as zipfp: self.assertRaises(struct.error, zipfp.write, TESTFN) - with zipfile.ZipFile(TESTFN2, "w", strict_timestamps=False) as zipfp: + with zipfile2.ZipFile(TESTFN2, "w", strict_timestamps=False) as zipfp: zipfp.write(TESTFN) zinfo = zipfp.getinfo(TESTFN) self.assertEqual(zinfo.date_time, (2107, 12, 31, 23, 59, 59)) @@ -637,7 +639,7 @@ class DeflateTestsWithSourceFile(AbstractTestsWithSourceFile, def test_per_file_compression(self): """Check that files within a Zip archive can have different compression options.""" - with zipfile.ZipFile(TESTFN2, "w") as zipfp: + with zipfile2.ZipFile(TESTFN2, "w") as zipfp: zipfp.write(TESTFN, 'storeme', zipfile.ZIP_STORED) zipfp.write(TESTFN, 'deflateme', zipfile.ZIP_DEFLATED) sinfo = zipfp.getinfo('storeme') @@ -678,13 +680,13 @@ def setUp(self): def zip_test(self, f, compression): # Create the ZIP archive - with zipfile.ZipFile(f, "w", compression, allowZip64=True) as zipfp: + with zipfile2.ZipFile(f, "w", compression, allowZip64=True) as zipfp: zipfp.write(TESTFN, "another.name") zipfp.write(TESTFN, TESTFN) zipfp.writestr("strfile", self.data) # Read the ZIP archive - with zipfile.ZipFile(f, "r", compression) as zipfp: + with zipfile2.ZipFile(f, "r", compression) as zipfp: self.assertEqual(zipfp.read(TESTFN), self.data) self.assertEqual(zipfp.read("another.name"), self.data) self.assertEqual(zipfp.read("strfile"), self.data) @@ -740,7 +742,7 @@ def test_basic(self): def test_too_many_files(self): # This test checks that more than 64k files can be added to an archive, # and that the resulting archive can be read properly by ZipFile - zipf = zipfile.ZipFile(TESTFN, "w", self.compression, + zipf = zipfile2.ZipFile(TESTFN, "w", self.compression, allowZip64=True) zipf.debug = 100 numfiles = 15 @@ -749,7 +751,7 @@ def test_too_many_files(self): self.assertEqual(len(zipf.namelist()), numfiles) zipf.close() - zipf2 = zipfile.ZipFile(TESTFN, "r", self.compression) + zipf2 = zipfile2.ZipFile(TESTFN, "r", self.compression) self.assertEqual(len(zipf2.namelist()), numfiles) for i in range(numfiles): content = zipf2.read("foo%08d" % i).decode('ascii') @@ -757,7 +759,7 @@ def test_too_many_files(self): zipf2.close() def test_too_many_files_append(self): - zipf = zipfile.ZipFile(TESTFN, "w", self.compression, + zipf = zipfile2.ZipFile(TESTFN, "w", self.compression, allowZip64=False) zipf.debug = 100 numfiles = 9 @@ -769,7 +771,7 @@ def test_too_many_files_append(self): self.assertEqual(len(zipf.namelist()), numfiles) zipf.close() - zipf = zipfile.ZipFile(TESTFN, "a", self.compression, + zipf = zipfile2.ZipFile(TESTFN, "a", self.compression, allowZip64=False) zipf.debug = 100 self.assertEqual(len(zipf.namelist()), numfiles) @@ -778,7 +780,7 @@ def test_too_many_files_append(self): self.assertEqual(len(zipf.namelist()), numfiles) zipf.close() - zipf = zipfile.ZipFile(TESTFN, "a", self.compression, + zipf = zipfile2.ZipFile(TESTFN, "a", self.compression, allowZip64=True) zipf.debug = 100 self.assertEqual(len(zipf.namelist()), numfiles) @@ -788,7 +790,7 @@ def test_too_many_files_append(self): self.assertEqual(len(zipf.namelist()), numfiles2) zipf.close() - zipf2 = zipfile.ZipFile(TESTFN, "r", self.compression) + zipf2 = zipfile2.ZipFile(TESTFN, "r", self.compression) self.assertEqual(len(zipf2.namelist()), numfiles2) for i in range(numfiles2): content = zipf2.read("foo%08d" % i).decode('ascii') @@ -807,12 +809,12 @@ class StoredTestZip64InSmallFiles(AbstractTestZip64InSmallFiles, compression = zipfile.ZIP_STORED def large_file_exception_test(self, f, compression): - with zipfile.ZipFile(f, "w", compression, allowZip64=False) as zipfp: + with zipfile2.ZipFile(f, "w", compression, allowZip64=False) as zipfp: self.assertRaises(zipfile.LargeZipFile, zipfp.write, TESTFN, "another.name") def large_file_exception_test2(self, f, compression): - with zipfile.ZipFile(f, "w", compression, allowZip64=False) as zipfp: + with zipfile2.ZipFile(f, "w", compression, allowZip64=False) as zipfp: self.assertRaises(zipfile.LargeZipFile, zipfp.writestr, "another.name", self.data) @@ -822,24 +824,24 @@ def test_large_file_exception(self): self.large_file_exception_test2(f, zipfile.ZIP_STORED) def test_absolute_arcnames(self): - with zipfile.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED, + with zipfile2.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED, allowZip64=True) as zipfp: zipfp.write(TESTFN, "/absolute") - with zipfile.ZipFile(TESTFN2, "r", zipfile.ZIP_STORED) as zipfp: + with zipfile2.ZipFile(TESTFN2, "r", zipfile.ZIP_STORED) as zipfp: self.assertEqual(zipfp.namelist(), ["absolute"]) def test_append(self): # Test that appending to the Zip64 archive doesn't change # extra fields of existing entries. - with zipfile.ZipFile(TESTFN2, "w", allowZip64=True) as zipfp: + with zipfile2.ZipFile(TESTFN2, "w", allowZip64=True) as zipfp: zipfp.writestr("strfile", self.data) - with zipfile.ZipFile(TESTFN2, "r", allowZip64=True) as zipfp: + with zipfile2.ZipFile(TESTFN2, "r", allowZip64=True) as zipfp: zinfo = zipfp.getinfo("strfile") extra = zinfo.extra - with zipfile.ZipFile(TESTFN2, "a", allowZip64=True) as zipfp: + with zipfile2.ZipFile(TESTFN2, "a", allowZip64=True) as zipfp: zipfp.writestr("strfile2", self.data) - with zipfile.ZipFile(TESTFN2, "r", allowZip64=True) as zipfp: + with zipfile2.ZipFile(TESTFN2, "r", allowZip64=True) as zipfp: zinfo = zipfp.getinfo("strfile") self.assertEqual(zinfo.extra, extra) @@ -964,7 +966,7 @@ def test_bad_zip64_extra(self): file_size_64_set=True, ) with self.assertRaises(zipfile.BadZipFile) as e: - zipfile.ZipFile(io.BytesIO(missing_file_size_extra)) + zipfile2.ZipFile(io.BytesIO(missing_file_size_extra)) self.assertIn('file size', str(e.exception).lower()) # zip64 file size present, zip64 compress size present, one field in @@ -975,7 +977,7 @@ def test_bad_zip64_extra(self): compress_size_64_set=True, ) with self.assertRaises(zipfile.BadZipFile) as e: - zipfile.ZipFile(io.BytesIO(missing_compress_size_extra)) + zipfile2.ZipFile(io.BytesIO(missing_compress_size_extra)) self.assertIn('compress size', str(e.exception).lower()) # zip64 compress size present, no fields in extra, expecting one, @@ -984,7 +986,7 @@ def test_bad_zip64_extra(self): compress_size_64_set=True, ) with self.assertRaises(zipfile.BadZipFile) as e: - zipfile.ZipFile(io.BytesIO(missing_compress_size_extra)) + zipfile2.ZipFile(io.BytesIO(missing_compress_size_extra)) self.assertIn('compress size', str(e.exception).lower()) # zip64 file size present, zip64 compress size present, zip64 header @@ -998,7 +1000,7 @@ def test_bad_zip64_extra(self): header_offset_64_set=True, ) with self.assertRaises(zipfile.BadZipFile) as e: - zipfile.ZipFile(io.BytesIO(missing_header_offset_extra)) + zipfile2.ZipFile(io.BytesIO(missing_header_offset_extra)) self.assertIn('header offset', str(e.exception).lower()) # zip64 compress size present, zip64 header offset present, one field @@ -1010,7 +1012,7 @@ def test_bad_zip64_extra(self): header_offset_64_set=True, ) with self.assertRaises(zipfile.BadZipFile) as e: - zipfile.ZipFile(io.BytesIO(missing_header_offset_extra)) + zipfile2.ZipFile(io.BytesIO(missing_header_offset_extra)) self.assertIn('header offset', str(e.exception).lower()) # zip64 file size present, zip64 header offset present, one field in @@ -1022,7 +1024,7 @@ def test_bad_zip64_extra(self): header_offset_64_set=True, ) with self.assertRaises(zipfile.BadZipFile) as e: - zipfile.ZipFile(io.BytesIO(missing_header_offset_extra)) + zipfile2.ZipFile(io.BytesIO(missing_header_offset_extra)) self.assertIn('header offset', str(e.exception).lower()) # zip64 header offset present, no fields in extra, expecting one, @@ -1033,7 +1035,7 @@ def test_bad_zip64_extra(self): header_offset_64_set=True, ) with self.assertRaises(zipfile.BadZipFile) as e: - zipfile.ZipFile(io.BytesIO(missing_header_offset_extra)) + zipfile2.ZipFile(io.BytesIO(missing_header_offset_extra)) self.assertIn('header offset', str(e.exception).lower()) def test_generated_valid_zip64_extra(self): @@ -1056,7 +1058,7 @@ def test_generated_valid_zip64_extra(self): kwargs = {} for c in combo: kwargs.update(c) - with zipfile.ZipFile(io.BytesIO(self.make_zip64_file(**kwargs))) as zf: + with zipfile2.ZipFile(io.BytesIO(self.make_zip64_file(**kwargs))) as zf: zinfo = zf.infolist()[0] self.assertEqual(zinfo.file_size, expected_file_size) self.assertEqual(zinfo.compress_size, expected_compress_size) @@ -1087,7 +1089,7 @@ def tearDown(self): def test_close_after_close(self): data = b'content' - with zipfile.ZipFile(TESTFN2, "w", self.compression) as zipf: + with zipfile2.ZipFile(TESTFN2, "w", self.compression) as zipf: w = zipf.open('test', 'w') w.write(data) w.close() @@ -1098,7 +1100,7 @@ def test_close_after_close(self): def test_write_after_close(self): data = b'content' - with zipfile.ZipFile(TESTFN2, "w", self.compression) as zipf: + with zipfile2.ZipFile(TESTFN2, "w", self.compression) as zipf: w = zipf.open('test', 'w') w.write(data) w.close() @@ -1318,14 +1320,14 @@ def test_write_pathlike(self): class ExtractTests(unittest.TestCase): def make_test_file(self): - with zipfile.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED) as zipfp: + with zipfile2.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED) as zipfp: for fpath, fdata in SMALL_TEST_DATA: zipfp.writestr(fpath, fdata) def test_extract(self): with temp_cwd(): self.make_test_file() - with zipfile.ZipFile(TESTFN2, "r") as zipfp: + with zipfile2.ZipFile(TESTFN2, "r") as zipfp: for fpath, fdata in SMALL_TEST_DATA: writtenfile = zipfp.extract(fpath) @@ -1343,7 +1345,7 @@ def test_extract(self): def _test_extract_with_target(self, target): self.make_test_file() - with zipfile.ZipFile(TESTFN2, "r") as zipfp: + with zipfile2.ZipFile(TESTFN2, "r") as zipfp: for fpath, fdata in SMALL_TEST_DATA: writtenfile = zipfp.extract(fpath, target) @@ -1371,7 +1373,7 @@ def test_extract_with_target_pathlike(self): def test_extract_all(self): with temp_cwd(): self.make_test_file() - with zipfile.ZipFile(TESTFN2, "r") as zipfp: + with zipfile2.ZipFile(TESTFN2, "r") as zipfp: zipfp.extractall() for fpath, fdata in SMALL_TEST_DATA: outfile = os.path.join(os.getcwd(), fpath) @@ -1383,7 +1385,7 @@ def test_extract_all(self): def _test_extract_all_with_target(self, target): self.make_test_file() - with zipfile.ZipFile(TESTFN2, "r") as zipfp: + with zipfile2.ZipFile(TESTFN2, "r") as zipfp: zipfp.extractall(target) for fpath, fdata in SMALL_TEST_DATA: outfile = os.path.join(target, fpath) @@ -1409,7 +1411,7 @@ def check_file(self, filename, content): self.assertEqual(f.read(), content) def test_sanitize_windows_name(self): - san = zipfile.ZipFile._sanitize_windows_name + san = zipfile2.ZipFile._sanitize_windows_name # Passing pathsep in allows this test to work regardless of platform. self.assertEqual(san(r',,?,C:,foo,bar/z', ','), r'_,C_,foo,bar/z') self.assertEqual(san(r'a\b,ce|f"g?h*i', ','), r'a\b,c_d_e_f_g_h_i') @@ -1466,7 +1468,7 @@ def test_extract_hackers_arcnames_posix_only(self): def _test_extract_hackers_arcnames(self, hacknames): for arcname, fixedname in hacknames: content = b'foobar' + arcname.encode() - with zipfile.ZipFile(TESTFN2, 'w', zipfile.ZIP_STORED) as zipfp: + with zipfile2.ZipFile(TESTFN2, 'w', zipfile.ZIP_STORED) as zipfp: zinfo = zipfile.ZipInfo() # preserve backslashes zinfo.filename = arcname @@ -1477,7 +1479,7 @@ def _test_extract_hackers_arcnames(self, hacknames): targetpath = os.path.join('target', 'subdir', 'subsub') correctfile = os.path.join(targetpath, *fixedname.split('/')) - with zipfile.ZipFile(TESTFN2, 'r') as zipfp: + with zipfile2.ZipFile(TESTFN2, 'r') as zipfp: writtenfile = zipfp.extract(arcname, targetpath) self.assertEqual(writtenfile, correctfile, msg='extract %r: %r != %r' % @@ -1485,21 +1487,21 @@ def _test_extract_hackers_arcnames(self, hacknames): self.check_file(correctfile, content) rmtree('target') - with zipfile.ZipFile(TESTFN2, 'r') as zipfp: + with zipfile2.ZipFile(TESTFN2, 'r') as zipfp: zipfp.extractall(targetpath) self.check_file(correctfile, content) rmtree('target') correctfile = os.path.join(os.getcwd(), *fixedname.split('/')) - with zipfile.ZipFile(TESTFN2, 'r') as zipfp: + with zipfile2.ZipFile(TESTFN2, 'r') as zipfp: writtenfile = zipfp.extract(arcname) self.assertEqual(writtenfile, correctfile, msg="extract %r" % arcname) self.check_file(correctfile, content) rmtree(fixedname.split('/')[0]) - with zipfile.ZipFile(TESTFN2, 'r') as zipfp: + with zipfile2.ZipFile(TESTFN2, 'r') as zipfp: zipfp.extractall() self.check_file(correctfile, content) rmtree(fixedname.split('/')[0]) @@ -1510,13 +1512,13 @@ def _test_extract_hackers_arcnames(self, hacknames): class OtherTests(unittest.TestCase): def test_open_via_zip_info(self): # Create the ZIP archive - with zipfile.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED) as zipfp: + with zipfile2.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED) as zipfp: zipfp.writestr("name", "foo") with self.assertWarns(UserWarning): zipfp.writestr("name", "bar") self.assertEqual(zipfp.namelist(), ["name"] * 2) - with zipfile.ZipFile(TESTFN2, "r") as zipfp: + with zipfile2.ZipFile(TESTFN2, "r") as zipfp: infos = zipfp.infolist() data = b"" for info in infos: @@ -1529,7 +1531,7 @@ def test_open_via_zip_info(self): self.assertIn(data, {b"foobar", b"barfoo"}) def test_writestr_extended_local_header_issue1202(self): - with zipfile.ZipFile(TESTFN2, 'w') as orig_zip: + with zipfile2.ZipFile(TESTFN2, 'w') as orig_zip: for data in 'abcdefghijklmnop': zinfo = zipfile.ZipInfo(data) zinfo.flag_bits |= 0x08 # Include an extended local header. @@ -1537,25 +1539,25 @@ def test_writestr_extended_local_header_issue1202(self): def test_close(self): """Check that the zipfile is closed after the 'with' block.""" - with zipfile.ZipFile(TESTFN2, "w") as zipfp: + with zipfile2.ZipFile(TESTFN2, "w") as zipfp: for fpath, fdata in SMALL_TEST_DATA: zipfp.writestr(fpath, fdata) self.assertIsNotNone(zipfp.fp, 'zipfp is not open') self.assertIsNone(zipfp.fp, 'zipfp is not closed') - with zipfile.ZipFile(TESTFN2, "r") as zipfp: + with zipfile2.ZipFile(TESTFN2, "r") as zipfp: self.assertIsNotNone(zipfp.fp, 'zipfp is not open') self.assertIsNone(zipfp.fp, 'zipfp is not closed') def test_close_on_exception(self): """Check that the zipfile is closed if an exception is raised in the 'with' block.""" - with zipfile.ZipFile(TESTFN2, "w") as zipfp: + with zipfile2.ZipFile(TESTFN2, "w") as zipfp: for fpath, fdata in SMALL_TEST_DATA: zipfp.writestr(fpath, fdata) try: - with zipfile.ZipFile(TESTFN2, "r") as zipfp2: + with zipfile2.ZipFile(TESTFN2, "r") as zipfp2: raise zipfile.BadZipFile() except zipfile.BadZipFile: self.assertIsNone(zipfp2.fp, 'zipfp is not closed') @@ -1568,29 +1570,29 @@ def test_unsupported_version(self): b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x01\x00\x00\x00\x00xPK\x05\x06' b'\x00\x00\x00\x00\x01\x00\x01\x00/\x00\x00\x00\x1f\x00\x00\x00\x00\x00') - self.assertRaises(NotImplementedError, zipfile.ZipFile, + self.assertRaises(NotImplementedError, zipfile2.ZipFile, io.BytesIO(data), 'r') @requires_zlib def test_read_unicode_filenames(self): # bug #10801 fname = findfile('zip_cp437_header.zip') - with zipfile.ZipFile(fname) as zipfp: + with zipfile2.ZipFile(fname) as zipfp: for name in zipfp.namelist(): zipfp.open(name).close() def test_write_unicode_filenames(self): - with zipfile.ZipFile(TESTFN, "w") as zf: + with zipfile2.ZipFile(TESTFN, "w") as zf: zf.writestr("foo.txt", "Test for unicode filename") zf.writestr("\xf6.txt", "Test for unicode filename") self.assertIsInstance(zf.infolist()[0].filename, str) - with zipfile.ZipFile(TESTFN, "r") as zf: + with zipfile2.ZipFile(TESTFN, "r") as zf: self.assertEqual(zf.filelist[0].filename, "foo.txt") self.assertEqual(zf.filelist[1].filename, "\xf6.txt") def test_read_after_write_unicode_filenames(self): - with zipfile.ZipFile(TESTFN2, 'w') as zipfp: + with zipfile2.ZipFile(TESTFN2, 'w') as zipfp: zipfp.writestr('приклад', b'sample') self.assertEqual(zipfp.read('приклад'), b'sample') @@ -1599,11 +1601,11 @@ def test_exclusive_create_zip_file(self): unlink(TESTFN2) filename = 'testfile.txt' content = b'hello, world. this is some content.' - with zipfile.ZipFile(TESTFN2, "x", zipfile.ZIP_STORED) as zipfp: + with zipfile2.ZipFile(TESTFN2, "x", zipfile.ZIP_STORED) as zipfp: zipfp.writestr(filename, content) with self.assertRaises(FileExistsError): - zipfile.ZipFile(TESTFN2, "x", zipfile.ZIP_STORED) - with zipfile.ZipFile(TESTFN2, "r") as zipfp: + zipfile2.ZipFile(TESTFN2, "x", zipfile.ZIP_STORED) + with zipfile2.ZipFile(TESTFN2, "r") as zipfp: self.assertEqual(zipfp.namelist(), [filename]) self.assertEqual(zipfp.read(filename), content) @@ -1615,14 +1617,14 @@ def test_create_non_existent_file_for_append(self): content = b'hello, world. this is some content.' try: - with zipfile.ZipFile(TESTFN, 'a') as zf: + with zipfile2.ZipFile(TESTFN, 'a') as zf: zf.writestr(filename, content) except OSError: self.fail('Could not append data to a non-existent zip file.') self.assertTrue(os.path.exists(TESTFN)) - with zipfile.ZipFile(TESTFN, 'r') as zf: + with zipfile2.ZipFile(TESTFN, 'r') as zf: self.assertEqual(zf.read(filename), content) def test_close_erroneous_file(self): @@ -1636,7 +1638,7 @@ def test_close_erroneous_file(self): with open(TESTFN, "w") as fp: fp.write("this is not a legal zip file\n") try: - zf = zipfile.ZipFile(TESTFN) + zf = zipfile2.ZipFile(TESTFN) except zipfile.BadZipFile: pass @@ -1662,7 +1664,7 @@ def test_damaged_zipfile(self): """Check that zipfiles with missing bytes at the end raise BadZipFile.""" # - Create a valid zip file fp = io.BytesIO() - with zipfile.ZipFile(fp, mode="w") as zipf: + with zipfile2.ZipFile(fp, mode="w") as zipf: zipf.writestr("foo.txt", b"O, for a Muse of Fire!") zipfiledata = fp.getvalue() @@ -1670,12 +1672,12 @@ def test_damaged_zipfile(self): # a BadZipFile exception is raised when we try to open it for N in range(len(zipfiledata)): fp = io.BytesIO(zipfiledata[:N]) - self.assertRaises(zipfile.BadZipFile, zipfile.ZipFile, fp) + self.assertRaises(zipfile.BadZipFile, zipfile2.ZipFile, fp) def test_is_zip_valid_file(self): """Check that is_zipfile() correctly identifies zip files.""" # - passing a filename - with zipfile.ZipFile(TESTFN, mode="w") as zipf: + with zipfile2.ZipFile(TESTFN, mode="w") as zipf: zipf.writestr("foo.txt", b"O, for a Muse of Fire!") self.assertTrue(zipfile.is_zipfile(TESTFN)) @@ -1703,21 +1705,21 @@ def test_non_existent_file_raises_OSError(self): # it is ignored, but the user should be sufficiently annoyed by # the message on the output that regression will be noticed # quickly. - self.assertRaises(OSError, zipfile.ZipFile, TESTFN) + self.assertRaises(OSError, zipfile2.ZipFile, TESTFN) def test_empty_file_raises_BadZipFile(self): f = open(TESTFN, 'w') f.close() - self.assertRaises(zipfile.BadZipFile, zipfile.ZipFile, TESTFN) + self.assertRaises(zipfile.BadZipFile, zipfile2.ZipFile, TESTFN) with open(TESTFN, 'w') as fp: fp.write("short file") - self.assertRaises(zipfile.BadZipFile, zipfile.ZipFile, TESTFN) + self.assertRaises(zipfile.BadZipFile, zipfile2.ZipFile, TESTFN) def test_closed_zip_raises_ValueError(self): """Verify that testzip() doesn't swallow inappropriate exceptions.""" data = io.BytesIO() - with zipfile.ZipFile(data, mode="w") as zipf: + with zipfile2.ZipFile(data, mode="w") as zipf: zipf.writestr("foo.txt", "O, for a Muse of Fire!") # This is correct; calling .read on a closed ZipFile should raise @@ -1734,14 +1736,14 @@ def test_closed_zip_raises_ValueError(self): def test_bad_constructor_mode(self): """Check that bad modes passed to ZipFile constructor are caught.""" - self.assertRaises(ValueError, zipfile.ZipFile, TESTFN, "q") + self.assertRaises(ValueError, zipfile2.ZipFile, TESTFN, "q") def test_bad_open_mode(self): """Check that bad modes passed to ZipFile.open are caught.""" - with zipfile.ZipFile(TESTFN, mode="w") as zipf: + with zipfile2.ZipFile(TESTFN, mode="w") as zipf: zipf.writestr("foo.txt", "O, for a Muse of Fire!") - with zipfile.ZipFile(TESTFN, mode="r") as zipf: + with zipfile2.ZipFile(TESTFN, mode="r") as zipf: # read the data to make sure the file is there zipf.read("foo.txt") self.assertRaises(ValueError, zipf.open, "foo.txt", "q") @@ -1752,7 +1754,7 @@ def test_bad_open_mode(self): def test_read0(self): """Check that calling read(0) on a ZipExtFile object returns an empty string and doesn't advance file pointer.""" - with zipfile.ZipFile(TESTFN, mode="w") as zipf: + with zipfile2.ZipFile(TESTFN, mode="w") as zipf: zipf.writestr("foo.txt", "O, for a Muse of Fire!") # read the data to make sure the file is there with zipf.open("foo.txt") as f: @@ -1764,13 +1766,13 @@ def test_read0(self): def test_open_non_existent_item(self): """Check that attempting to call open() for an item that doesn't exist in the archive raises a RuntimeError.""" - with zipfile.ZipFile(TESTFN, mode="w") as zipf: + with zipfile2.ZipFile(TESTFN, mode="w") as zipf: self.assertRaises(KeyError, zipf.open, "foo.txt", "r") def test_bad_compression_mode(self): """Check that bad compression methods passed to ZipFile.open are caught.""" - self.assertRaises(NotImplementedError, zipfile.ZipFile, TESTFN, "w", -1) + self.assertRaises(NotImplementedError, zipfile2.ZipFile, TESTFN, "w", -1) def test_unsupported_compression(self): # data is declared as shrunk, but actually deflated @@ -1780,13 +1782,13 @@ def test_unsupported_compression(self): b'\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' b'\x80\x01\x00\x00\x00\x00xPK\x05\x06\x00\x00\x00\x00\x01\x00\x01\x00' b'/\x00\x00\x00!\x00\x00\x00\x00\x00') - with zipfile.ZipFile(io.BytesIO(data), 'r') as zipf: + with zipfile2.ZipFile(io.BytesIO(data), 'r') as zipf: self.assertRaises(NotImplementedError, zipf.open, 'x') def test_null_byte_in_filename(self): """Check that a filename containing a null byte is properly terminated.""" - with zipfile.ZipFile(TESTFN, mode="w") as zipf: + with zipfile2.ZipFile(TESTFN, mode="w") as zipf: zipf.writestr("foo.txt\x00qqq", b"O, for a Muse of Fire!") self.assertEqual(zipf.namelist(), ['foo.txt']) @@ -1801,96 +1803,96 @@ def test_comments(self): """Check that comments on the archive are handled properly.""" # check default comment is empty - with zipfile.ZipFile(TESTFN, mode="w") as zipf: + with zipfile2.ZipFile(TESTFN, mode="w") as zipf: self.assertEqual(zipf.comment, b'') zipf.writestr("foo.txt", "O, for a Muse of Fire!") - with zipfile.ZipFile(TESTFN, mode="r") as zipfr: + with zipfile2.ZipFile(TESTFN, mode="r") as zipfr: self.assertEqual(zipfr.comment, b'') # check a simple short comment comment = b'Bravely taking to his feet, he beat a very brave retreat.' - with zipfile.ZipFile(TESTFN, mode="w") as zipf: + with zipfile2.ZipFile(TESTFN, mode="w") as zipf: zipf.comment = comment zipf.writestr("foo.txt", "O, for a Muse of Fire!") - with zipfile.ZipFile(TESTFN, mode="r") as zipfr: + with zipfile2.ZipFile(TESTFN, mode="r") as zipfr: self.assertEqual(zipf.comment, comment) # check a comment of max length comment2 = ''.join(['%d' % (i**3 % 10) for i in range((1 << 16)-1)]) comment2 = comment2.encode("ascii") - with zipfile.ZipFile(TESTFN, mode="w") as zipf: + with zipfile2.ZipFile(TESTFN, mode="w") as zipf: zipf.comment = comment2 zipf.writestr("foo.txt", "O, for a Muse of Fire!") - with zipfile.ZipFile(TESTFN, mode="r") as zipfr: + with zipfile2.ZipFile(TESTFN, mode="r") as zipfr: self.assertEqual(zipfr.comment, comment2) # check a comment that is too long is truncated - with zipfile.ZipFile(TESTFN, mode="w") as zipf: + with zipfile2.ZipFile(TESTFN, mode="w") as zipf: with self.assertWarns(UserWarning): zipf.comment = comment2 + b'oops' zipf.writestr("foo.txt", "O, for a Muse of Fire!") - with zipfile.ZipFile(TESTFN, mode="r") as zipfr: + with zipfile2.ZipFile(TESTFN, mode="r") as zipfr: self.assertEqual(zipfr.comment, comment2) # check that comments are correctly modified in append mode - with zipfile.ZipFile(TESTFN,mode="w") as zipf: + with zipfile2.ZipFile(TESTFN,mode="w") as zipf: zipf.comment = b"original comment" zipf.writestr("foo.txt", "O, for a Muse of Fire!") - with zipfile.ZipFile(TESTFN,mode="a") as zipf: + with zipfile2.ZipFile(TESTFN,mode="a") as zipf: zipf.comment = b"an updated comment" - with zipfile.ZipFile(TESTFN,mode="r") as zipf: + with zipfile2.ZipFile(TESTFN,mode="r") as zipf: self.assertEqual(zipf.comment, b"an updated comment") # check that comments are correctly shortened in append mode # and the file is indeed truncated - with zipfile.ZipFile(TESTFN,mode="w") as zipf: + with zipfile2.ZipFile(TESTFN,mode="w") as zipf: zipf.comment = b"original comment that's longer" zipf.writestr("foo.txt", "O, for a Muse of Fire!") original_zip_size = os.path.getsize(TESTFN) - with zipfile.ZipFile(TESTFN,mode="a") as zipf: + with zipfile2.ZipFile(TESTFN,mode="a") as zipf: zipf.comment = b"shorter comment" self.assertTrue(original_zip_size > os.path.getsize(TESTFN)) - with zipfile.ZipFile(TESTFN,mode="r") as zipf: + with zipfile2.ZipFile(TESTFN,mode="r") as zipf: self.assertEqual(zipf.comment, b"shorter comment") def test_unicode_comment(self): - with zipfile.ZipFile(TESTFN, "w", zipfile.ZIP_STORED) as zipf: + with zipfile2.ZipFile(TESTFN, "w", zipfile.ZIP_STORED) as zipf: zipf.writestr("foo.txt", "O, for a Muse of Fire!") with self.assertRaises(TypeError): zipf.comment = "this is an error" def test_change_comment_in_empty_archive(self): - with zipfile.ZipFile(TESTFN, "a", zipfile.ZIP_STORED) as zipf: + with zipfile2.ZipFile(TESTFN, "a", zipfile.ZIP_STORED) as zipf: self.assertFalse(zipf.filelist) zipf.comment = b"this is a comment" - with zipfile.ZipFile(TESTFN, "r") as zipf: + with zipfile2.ZipFile(TESTFN, "r") as zipf: self.assertEqual(zipf.comment, b"this is a comment") def test_change_comment_in_nonempty_archive(self): - with zipfile.ZipFile(TESTFN, "w", zipfile.ZIP_STORED) as zipf: + with zipfile2.ZipFile(TESTFN, "w", zipfile.ZIP_STORED) as zipf: zipf.writestr("foo.txt", "O, for a Muse of Fire!") - with zipfile.ZipFile(TESTFN, "a", zipfile.ZIP_STORED) as zipf: + with zipfile2.ZipFile(TESTFN, "a", zipfile.ZIP_STORED) as zipf: self.assertTrue(zipf.filelist) zipf.comment = b"this is a comment" - with zipfile.ZipFile(TESTFN, "r") as zipf: + with zipfile2.ZipFile(TESTFN, "r") as zipf: self.assertEqual(zipf.comment, b"this is a comment") def test_empty_zipfile(self): # Check that creating a file in 'w' or 'a' mode and closing without # adding any files to the archives creates a valid empty ZIP file - zipf = zipfile.ZipFile(TESTFN, mode="w") + zipf = zipfile2.ZipFile(TESTFN, mode="w") zipf.close() try: - zipf = zipfile.ZipFile(TESTFN, mode="r") + zipf = zipfile2.ZipFile(TESTFN, mode="r") except zipfile.BadZipFile: self.fail("Unable to create empty ZIP file in 'w' mode") - zipf = zipfile.ZipFile(TESTFN, mode="a") + zipf = zipfile2.ZipFile(TESTFN, mode="a") zipf.close() try: - zipf = zipfile.ZipFile(TESTFN, mode="r") + zipf = zipfile2.ZipFile(TESTFN, mode="r") except: self.fail("Unable to create empty ZIP file in 'a' mode") @@ -1900,7 +1902,7 @@ def test_open_empty_file(self): # OSError) f = open(TESTFN, 'w') f.close() - self.assertRaises(zipfile.BadZipFile, zipfile.ZipFile, TESTFN, 'r') + self.assertRaises(zipfile.BadZipFile, zipfile2.ZipFile, TESTFN, 'r') def test_create_zipinfo_before_1980(self): self.assertRaises(ValueError, @@ -1917,7 +1919,7 @@ def test_zipfile_with_short_extra_field(self): b'\x00\x00\x00abc\x00\x00PK\x05\x06\x00\x00\x00\x00' b'\x01\x00\x01\x003\x00\x00\x00%\x00\x00\x00\x00\x00' ) - with zipfile.ZipFile(io.BytesIO(zipdata), 'r') as zipf: + with zipfile2.ZipFile(io.BytesIO(zipdata), 'r') as zipf: # testzip returns the name of the first corrupt file, or None self.assertIsNone(zipf.testzip()) @@ -1926,7 +1928,7 @@ def test_open_conflicting_handles(self): msg1 = b"It's fun to charter an accountant!" msg2 = b"And sail the wide accountant sea" msg3 = b"To find, explore the funds offshore" - with zipfile.ZipFile(TESTFN2, 'w', zipfile.ZIP_STORED) as zipf: + with zipfile2.ZipFile(TESTFN2, 'w', zipfile.ZIP_STORED) as zipf: with zipf.open('foo', mode='w') as w2: w2.write(msg1) with zipf.open('bar', mode='w') as w1: @@ -1944,7 +1946,7 @@ def test_open_conflicting_handles(self): with zipf.open('baz', mode='w') as w2: w2.write(msg3) - with zipfile.ZipFile(TESTFN2, 'r') as zipf: + with zipfile2.ZipFile(TESTFN2, 'r') as zipf: self.assertEqual(zipf.read('foo'), msg1) self.assertEqual(zipf.read('bar'), msg2) self.assertEqual(zipf.read('baz'), msg3) @@ -1955,9 +1957,9 @@ def test_seek_tell(self): txt = b"Where's Bruce?" bloc = txt.find(b"Bruce") # Check seek on a file - with zipfile.ZipFile(TESTFN, "w") as zipf: + with zipfile2.ZipFile(TESTFN, "w") as zipf: zipf.writestr("foo.txt", txt) - with zipfile.ZipFile(TESTFN, "r") as zipf: + with zipfile2.ZipFile(TESTFN, "r") as zipf: with zipf.open("foo.txt", "r") as fp: fp.seek(bloc, os.SEEK_SET) self.assertEqual(fp.tell(), bloc) @@ -1972,9 +1974,9 @@ def test_seek_tell(self): self.assertEqual(fp.tell(), 0) # Check seek on memory file data = io.BytesIO() - with zipfile.ZipFile(data, mode="w") as zipf: + with zipfile2.ZipFile(data, mode="w") as zipf: zipf.writestr("foo.txt", txt) - with zipfile.ZipFile(data, mode="r") as zipf: + with zipfile2.ZipFile(data, mode="r") as zipf: with zipf.open("foo.txt", "r") as fp: fp.seek(bloc, os.SEEK_SET) self.assertEqual(fp.tell(), bloc) @@ -1992,10 +1994,10 @@ def test_seek_tell(self): def test_decompress_without_3rd_party_library(self): data = b'PK\x05\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' zip_file = io.BytesIO(data) - with zipfile.ZipFile(zip_file, 'w', compression=zipfile.ZIP_BZIP2) as zf: + with zipfile2.ZipFile(zip_file, 'w', compression=zipfile.ZIP_BZIP2) as zf: zf.writestr('a.txt', b'a') with mock.patch('zipfile.bz2', None): - with zipfile.ZipFile(zip_file) as zf: + with zipfile2.ZipFile(zip_file) as zf: self.assertRaises(RuntimeError, zf.extract, 'a.txt') @requires_zlib @@ -2013,7 +2015,7 @@ def test_full_overlap(self): b'\x06\x00\x00\x00\x00\x02\x00\x02\x00^\x00\x00\x00/\x00\x00' b'\x00\x00\x00' ) - with zipfile.ZipFile(io.BytesIO(data), 'r') as zipf: + with zipfile2.ZipFile(io.BytesIO(data), 'r') as zipf: self.assertEqual(zipf.namelist(), ['a', 'b']) zi = zipf.getinfo('a') self.assertEqual(zi.header_offset, 0) @@ -2044,7 +2046,7 @@ def test_quoted_overlap(self): b'bPK\x05\x06\x00\x00\x00\x00\x02\x00\x02\x00^\x00\x00' b'\x00S\x00\x00\x00\x00\x00' ) - with zipfile.ZipFile(io.BytesIO(data), 'r') as zipf: + with zipfile2.ZipFile(io.BytesIO(data), 'r') as zipf: self.assertEqual(zipf.namelist(), ['a', 'b']) zi = zipf.getinfo('a') self.assertEqual(zi.header_offset, 0) @@ -2068,7 +2070,7 @@ def test_testzip_with_bad_crc(self): """Tests that files with bad CRCs return their name from testzip.""" zipdata = self.zip_with_bad_crc - with zipfile.ZipFile(io.BytesIO(zipdata), mode="r") as zipf: + with zipfile2.ZipFile(io.BytesIO(zipdata), mode="r") as zipf: # testzip returns the name of the first corrupt file, or None self.assertEqual('afile', zipf.testzip()) @@ -2077,16 +2079,16 @@ def test_read_with_bad_crc(self): zipdata = self.zip_with_bad_crc # Using ZipFile.read() - with zipfile.ZipFile(io.BytesIO(zipdata), mode="r") as zipf: + with zipfile2.ZipFile(io.BytesIO(zipdata), mode="r") as zipf: self.assertRaises(zipfile.BadZipFile, zipf.read, 'afile') # Using ZipExtFile.read() - with zipfile.ZipFile(io.BytesIO(zipdata), mode="r") as zipf: + with zipfile2.ZipFile(io.BytesIO(zipdata), mode="r") as zipf: with zipf.open('afile', 'r') as corrupt_file: self.assertRaises(zipfile.BadZipFile, corrupt_file.read) # Same with small reads (in order to exercise the buffering logic) - with zipfile.ZipFile(io.BytesIO(zipdata), mode="r") as zipf: + with zipfile2.ZipFile(io.BytesIO(zipdata), mode="r") as zipf: with zipf.open('afile', 'r') as corrupt_file: corrupt_file.MIN_READ_SIZE = 2 with self.assertRaises(zipfile.BadZipFile): @@ -2179,10 +2181,10 @@ class DecryptionTests(unittest.TestCase): def setUp(self): with open(TESTFN, "wb") as fp: fp.write(self.data) - self.zip = zipfile.ZipFile(TESTFN, "r") + self.zip = zipfile2.ZipFile(TESTFN, "r") with open(TESTFN2, "wb") as fp: fp.write(self.data2) - self.zip2 = zipfile.ZipFile(TESTFN2, "r") + self.zip2 = zipfile2.ZipFile(TESTFN2, "r") def tearDown(self): self.zip.close() @@ -2271,7 +2273,7 @@ def tearDown(self): def make_test_archive(self, f, compression): # Create the ZIP archive - with zipfile.ZipFile(f, "w", compression) as zipfp: + with zipfile2.ZipFile(f, "w", compression) as zipfp: zipfp.write(TESTFN, "another.name") zipfp.write(TESTFN, TESTFN) @@ -2279,7 +2281,7 @@ def zip_test(self, f, compression): self.make_test_archive(f, compression) # Read the ZIP archive - with zipfile.ZipFile(f, "r", compression) as zipfp: + with zipfile2.ZipFile(f, "r", compression) as zipfp: testdata = zipfp.read(TESTFN) self.assertEqual(len(testdata), len(self.data)) self.assertEqual(testdata, self.data) @@ -2293,7 +2295,7 @@ def zip_open_test(self, f, compression): self.make_test_archive(f, compression) # Read the ZIP archive - with zipfile.ZipFile(f, "r", compression) as zipfp: + with zipfile2.ZipFile(f, "r", compression) as zipfp: zipdata1 = [] with zipfp.open(TESTFN) as zipopen1: while True: @@ -2326,7 +2328,7 @@ def zip_random_open_test(self, f, compression): self.make_test_archive(f, compression) # Read the ZIP archive - with zipfile.ZipFile(f, "r", compression) as zipfp: + with zipfile2.ZipFile(f, "r", compression) as zipfp: zipdata1 = [] with zipfp.open(TESTFN) as zipopen1: while True: @@ -2398,11 +2400,11 @@ def test_writestr(self): f = io.BytesIO() f.write(b'abc') bf = io.BufferedWriter(f) - with zipfile.ZipFile(wrapper(bf), 'w', zipfile.ZIP_STORED) as zipfp: + with zipfile2.ZipFile(wrapper(bf), 'w', zipfile.ZIP_STORED) as zipfp: zipfp.writestr('ones', b'111') zipfp.writestr('twos', b'222') self.assertEqual(f.getvalue()[:5], b'abcPK') - with zipfile.ZipFile(f, mode='r') as zipf: + with zipfile2.ZipFile(f, mode='r') as zipf: with zipf.open('ones') as zopen: self.assertEqual(zopen.read(), b'111') with zipf.open('twos') as zopen: @@ -2414,7 +2416,7 @@ def test_write(self): f = io.BytesIO() f.write(b'abc') bf = io.BufferedWriter(f) - with zipfile.ZipFile(wrapper(bf), 'w', zipfile.ZIP_STORED) as zipfp: + with zipfile2.ZipFile(wrapper(bf), 'w', zipfile.ZIP_STORED) as zipfp: self.addCleanup(unlink, TESTFN) with open(TESTFN, 'wb') as f2: f2.write(b'111') @@ -2423,7 +2425,7 @@ def test_write(self): f2.write(b'222') zipfp.write(TESTFN, 'twos') self.assertEqual(f.getvalue()[:5], b'abcPK') - with zipfile.ZipFile(f, mode='r') as zipf: + with zipfile2.ZipFile(f, mode='r') as zipf: with zipf.open('ones') as zopen: self.assertEqual(zopen.read(), b'111') with zipf.open('twos') as zopen: @@ -2435,13 +2437,13 @@ def test_open_write(self): f = io.BytesIO() f.write(b'abc') bf = io.BufferedWriter(f) - with zipfile.ZipFile(wrapper(bf), 'w', zipfile.ZIP_STORED) as zipf: + with zipfile2.ZipFile(wrapper(bf), 'w', zipfile.ZIP_STORED) as zipf: with zipf.open('ones', 'w') as zopen: zopen.write(b'111') with zipf.open('twos', 'w') as zopen: zopen.write(b'222') self.assertEqual(f.getvalue()[:5], b'abcPK') - with zipfile.ZipFile(f) as zipf: + with zipfile2.ZipFile(f) as zipf: self.assertEqual(zipf.read('ones'), b'111') self.assertEqual(zipf.read('twos'), b'222') @@ -2455,7 +2457,7 @@ def setUpClass(cls): def make_test_archive(self, f): # Create the ZIP archive - with zipfile.ZipFile(f, "w", zipfile.ZIP_DEFLATED) as zipfp: + with zipfile2.ZipFile(f, "w", zipfile.ZIP_DEFLATED) as zipfp: zipfp.writestr('ones', self.data1) zipfp.writestr('twos', self.data2) @@ -2464,7 +2466,7 @@ def test_same_file(self): # multiple open() calls can be made without interfering with each other. for f in get_files(self): self.make_test_archive(f) - with zipfile.ZipFile(f, mode="r") as zipf: + with zipfile2.ZipFile(f, mode="r") as zipf: with zipf.open('ones') as zopen1, zipf.open('ones') as zopen2: data1 = zopen1.read(500) data2 = zopen2.read(500) @@ -2478,7 +2480,7 @@ def test_different_file(self): # multiple open() calls can be made without interfering with each other. for f in get_files(self): self.make_test_archive(f) - with zipfile.ZipFile(f, mode="r") as zipf: + with zipfile2.ZipFile(f, mode="r") as zipf: with zipf.open('ones') as zopen1, zipf.open('twos') as zopen2: data1 = zopen1.read(500) data2 = zopen2.read(500) @@ -2492,7 +2494,7 @@ def test_interleaved(self): # multiple open() calls can be made without interfering with each other. for f in get_files(self): self.make_test_archive(f) - with zipfile.ZipFile(f, mode="r") as zipf: + with zipfile2.ZipFile(f, mode="r") as zipf: with zipf.open('ones') as zopen1: data1 = zopen1.read(500) with zipf.open('twos') as zopen2: @@ -2506,7 +2508,7 @@ def test_read_after_close(self): for f in get_files(self): self.make_test_archive(f) with contextlib.ExitStack() as stack: - with zipfile.ZipFile(f, 'r') as zipf: + with zipfile2.ZipFile(f, 'r') as zipf: zopen1 = stack.enter_context(zipf.open('ones')) zopen2 = stack.enter_context(zipf.open('twos')) data1 = zopen1.read(500) @@ -2518,13 +2520,13 @@ def test_read_after_close(self): def test_read_after_write(self): for f in get_files(self): - with zipfile.ZipFile(f, 'w', zipfile.ZIP_DEFLATED) as zipf: + with zipfile2.ZipFile(f, 'w', zipfile.ZIP_DEFLATED) as zipf: zipf.writestr('ones', self.data1) zipf.writestr('twos', self.data2) with zipf.open('ones') as zopen1: data1 = zopen1.read(500) self.assertEqual(data1, self.data1[:500]) - with zipfile.ZipFile(f, 'r') as zipf: + with zipfile2.ZipFile(f, 'r') as zipf: data1 = zipf.read('ones') data2 = zipf.read('twos') self.assertEqual(data1, self.data1) @@ -2532,12 +2534,12 @@ def test_read_after_write(self): def test_write_after_read(self): for f in get_files(self): - with zipfile.ZipFile(f, "w", zipfile.ZIP_DEFLATED) as zipf: + with zipfile2.ZipFile(f, "w", zipfile.ZIP_DEFLATED) as zipf: zipf.writestr('ones', self.data1) with zipf.open('ones') as zopen1: zopen1.read(500) zipf.writestr('twos', self.data2) - with zipfile.ZipFile(f, 'r') as zipf: + with zipfile2.ZipFile(f, 'r') as zipf: data1 = zipf.read('ones') data2 = zipf.read('twos') self.assertEqual(data1, self.data1) @@ -2547,7 +2549,7 @@ def test_many_opens(self): # Verify that read() and open() promptly close the file descriptor, # and don't rely on the garbage collector to free resources. self.make_test_archive(TESTFN2) - with zipfile.ZipFile(TESTFN2, mode="r") as zipf: + with zipfile2.ZipFile(TESTFN2, mode="r") as zipf: for x in range(100): zipf.read('ones') with zipf.open('ones') as zopen1: @@ -2556,16 +2558,16 @@ def test_many_opens(self): self.assertLess(f.fileno(), 100) def test_write_while_reading(self): - with zipfile.ZipFile(TESTFN2, 'w', zipfile.ZIP_DEFLATED) as zipf: + with zipfile2.ZipFile(TESTFN2, 'w', zipfile.ZIP_DEFLATED) as zipf: zipf.writestr('ones', self.data1) - with zipfile.ZipFile(TESTFN2, 'a', zipfile.ZIP_DEFLATED) as zipf: + with zipfile2.ZipFile(TESTFN2, 'a', zipfile.ZIP_DEFLATED) as zipf: with zipf.open('ones', 'r') as r1: data1 = r1.read(500) with zipf.open('twos', 'w') as w1: w1.write(self.data2) data1 += r1.read() self.assertEqual(data1, self.data1) - with zipfile.ZipFile(TESTFN2) as zipf: + with zipfile2.ZipFile(TESTFN2) as zipf: self.assertEqual(zipf.read('twos'), self.data2) def tearDown(self): @@ -2577,7 +2579,7 @@ def setUp(self): os.mkdir(TESTFN2) def test_extract_dir(self): - with zipfile.ZipFile(findfile("zipdir.zip")) as zipf: + with zipfile2.ZipFile(findfile("zipdir.zip")) as zipf: zipf.extractall(TESTFN2) self.assertTrue(os.path.isdir(os.path.join(TESTFN2, "a"))) self.assertTrue(os.path.isdir(os.path.join(TESTFN2, "a", "b"))) @@ -2592,7 +2594,7 @@ def test_write_dir(self): dirpath = os.path.join(TESTFN2, "x") os.mkdir(dirpath) mode = os.stat(dirpath).st_mode & 0xFFFF - with zipfile.ZipFile(TESTFN, "w") as zipf: + with zipfile2.ZipFile(TESTFN, "w") as zipf: zipf.write(dirpath) zinfo = zipf.filelist[0] self.assertTrue(zinfo.filename.endswith("/x/")) @@ -2601,7 +2603,7 @@ def test_write_dir(self): zinfo = zipf.filelist[1] self.assertTrue(zinfo.filename, "y/") self.assertEqual(zinfo.external_attr, (mode << 16) | 0x10) - with zipfile.ZipFile(TESTFN, "r") as zipf: + with zipfile2.ZipFile(TESTFN, "r") as zipf: zinfo = zipf.filelist[0] self.assertTrue(zinfo.filename.endswith("/x/")) self.assertEqual(zinfo.external_attr, (mode << 16) | 0x10) @@ -2616,12 +2618,12 @@ def test_write_dir(self): def test_writestr_dir(self): os.mkdir(os.path.join(TESTFN2, "x")) - with zipfile.ZipFile(TESTFN, "w") as zipf: + with zipfile2.ZipFile(TESTFN, "w") as zipf: zipf.writestr("x/", b'') zinfo = zipf.filelist[0] self.assertEqual(zinfo.filename, "x/") self.assertEqual(zinfo.external_attr, (0o40775 << 16) | 0x10) - with zipfile.ZipFile(TESTFN, "r") as zipf: + with zipfile2.ZipFile(TESTFN, "r") as zipf: zinfo = zipf.filelist[0] self.assertTrue(zinfo.filename.endswith("x/")) self.assertEqual(zinfo.external_attr, (0o40775 << 16) | 0x10) @@ -2704,7 +2706,7 @@ def test_test_command(self): def test_list_command(self): zip_name = findfile('zipdir.zip') t = io.StringIO() - with zipfile.ZipFile(zip_name, 'r') as tf: + with zipfile2.ZipFile(zip_name, 'r') as tf: tf.printdir(t) expected = t.getvalue().encode('ascii', 'backslashreplace') for opt in '-l', '--list': @@ -2727,7 +2729,7 @@ def test_create_command(self): try: out = self.zipfilecmd(opt, TESTFN2, *files) self.assertEqual(out, b'') - with zipfile.ZipFile(TESTFN2) as zf: + with zipfile2.ZipFile(TESTFN2) as zf: self.assertEqual(zf.namelist(), namelist) self.assertEqual(zf.read(namelist[0]), b'test 1') self.assertEqual(zf.read(namelist[2]), b'test 2') @@ -2740,7 +2742,7 @@ def test_extract_command(self): with temp_dir() as extdir: out = self.zipfilecmd(opt, zip_name, extdir) self.assertEqual(out, b'') - with zipfile.ZipFile(zip_name) as zf: + with zipfile2.ZipFile(zip_name) as zf: for zi in zf.infolist(): path = os.path.join(extdir, zi.filename.replace('/', os.sep)) @@ -2764,7 +2766,7 @@ def _test_zip_works(self, name): self.assertTrue(zipfile.is_zipfile(name), f'is_zipfile failed on {name}') # Ensure we can operate on these via ZipFile. - with zipfile.ZipFile(name) as zipfp: + with zipfile2.ZipFile(name) as zipfp: for n in zipfp.namelist(): data = zipfp.read(n) self.assertIn(b'FAVORITE_NUMBER', data) @@ -2847,7 +2849,7 @@ def build_alpharep_fixture(): "rep" because it's a representative example """ data = io.BytesIO() - zf = zipfile.ZipFile(data, "w") + zf = zipfile2.ZipFile(data, "w") zf.writestr("a.txt", b"content of a") zf.writestr("b/c.txt", b"content of c") zf.writestr("b/d/e.txt", b"content of e") @@ -2985,7 +2987,7 @@ def test_mutability(self): def huge_zipfile(self): """Create a read-only zipfile with a huge number of entries entries.""" strm = io.BytesIO() - zf = zipfile.ZipFile(strm, "w") + zf = zipfile2.ZipFile(strm, "w") for entry in map(str, range(self.HUGE_ZIPFILE_NUM_ENTRIES)): zf.writestr(entry, entry) zf.mode = 'r' @@ -3016,7 +3018,7 @@ def test_malformed_paths(self): Paths with dots are treated like regular files. """ data = io.BytesIO() - zf = zipfile.ZipFile(data, "w") + zf = zipfile2.ZipFile(data, "w") zf.writestr("/one-slash.txt", b"content") zf.writestr("//two-slash.txt", b"content") zf.writestr("../parent.txt", b"content") @@ -3034,7 +3036,7 @@ def test_unsupported_names(self): in the zip file. """ data = io.BytesIO() - zf = zipfile.ZipFile(data, "w") + zf = zipfile2.ZipFile(data, "w") zf.writestr("path?", b"content") zf.writestr("V: NMS.flac", b"fLaC...") zf.filename = '' @@ -3049,7 +3051,7 @@ def test_backslash_not_separator(self): In a zip file, backslashes are not separators. """ data = io.BytesIO() - zf = zipfile.ZipFile(data, "w") + zf = zipfile2.ZipFile(data, "w") zf.writestr(DirtyZipInfo.for_name("foo\\bar", zf), b"content") zf.filename = '' root = zipfile.Path(zf) diff --git a/cpython-tests/3.8/test_zipfile64.py b/cpython-tests/3.8/test_zipfile64.py index 56746bc..7b99aef 100644 --- a/cpython-tests/3.8/test_zipfile64.py +++ b/cpython-tests/3.8/test_zipfile64.py @@ -19,6 +19,8 @@ from test.support import TESTFN, requires_zlib +import zipfile2 + TESTFN2 = TESTFN + "2" # How much time in seconds can pass before we print a 'Still working' message. @@ -36,7 +38,7 @@ def setUp(self): def zipTest(self, f, compression): # Create the ZIP archive. - with zipfile.ZipFile(f, "w", compression) as zipfp: + with zipfile2.ZipFile(f, "w", compression) as zipfp: # It will contain enough copies of self.data to reach about 6 GiB of # raw data to store. @@ -54,7 +56,7 @@ def zipTest(self, f, compression): sys.__stdout__.flush() # Read the ZIP archive - with zipfile.ZipFile(f, "r", compression) as zipfp: + with zipfile2.ZipFile(f, "r", compression) as zipfp: for num in range(filecount): self.assertEqual(zipfp.read("testfn%d" % num), self.data) # Print still working message since this test can be really slow @@ -92,21 +94,21 @@ class OtherTests(unittest.TestCase): def testMoreThan64kFiles(self): # This test checks that more than 64k files can be added to an archive, # and that the resulting archive can be read properly by ZipFile - with zipfile.ZipFile(TESTFN, mode="w", allowZip64=True) as zipf: + with zipfile2.ZipFile(TESTFN, mode="w", allowZip64=True) as zipf: zipf.debug = 100 numfiles = (1 << 16) * 3//2 for i in range(numfiles): zipf.writestr("foo%08d" % i, "%d" % (i**3 % 57)) self.assertEqual(len(zipf.namelist()), numfiles) - with zipfile.ZipFile(TESTFN, mode="r") as zipf2: + with zipfile2.ZipFile(TESTFN, mode="r") as zipf2: self.assertEqual(len(zipf2.namelist()), numfiles) for i in range(numfiles): content = zipf2.read("foo%08d" % i).decode('ascii') self.assertEqual(content, "%d" % (i**3 % 57)) def testMoreThan64kFilesAppend(self): - with zipfile.ZipFile(TESTFN, mode="w", allowZip64=False) as zipf: + with zipfile2.ZipFile(TESTFN, mode="w", allowZip64=False) as zipf: zipf.debug = 100 numfiles = (1 << 16) - 1 for i in range(numfiles): @@ -116,14 +118,14 @@ def testMoreThan64kFilesAppend(self): zipf.writestr("foo%08d" % numfiles, b'') self.assertEqual(len(zipf.namelist()), numfiles) - with zipfile.ZipFile(TESTFN, mode="a", allowZip64=False) as zipf: + with zipfile2.ZipFile(TESTFN, mode="a", allowZip64=False) as zipf: zipf.debug = 100 self.assertEqual(len(zipf.namelist()), numfiles) with self.assertRaises(zipfile.LargeZipFile): zipf.writestr("foo%08d" % numfiles, b'') self.assertEqual(len(zipf.namelist()), numfiles) - with zipfile.ZipFile(TESTFN, mode="a", allowZip64=True) as zipf: + with zipfile2.ZipFile(TESTFN, mode="a", allowZip64=True) as zipf: zipf.debug = 100 self.assertEqual(len(zipf.namelist()), numfiles) numfiles2 = (1 << 16) * 3//2 @@ -131,7 +133,7 @@ def testMoreThan64kFilesAppend(self): zipf.writestr("foo%08d" % i, "%d" % (i**3 % 57)) self.assertEqual(len(zipf.namelist()), numfiles2) - with zipfile.ZipFile(TESTFN, mode="r") as zipf2: + with zipfile2.ZipFile(TESTFN, mode="r") as zipf2: self.assertEqual(len(zipf2.namelist()), numfiles2) for i in range(numfiles2): content = zipf2.read("foo%08d" % i).decode('ascii') diff --git a/cpython-tests/3.8/test_zipimport.py b/cpython-tests/3.8/test_zipimport.py index 2af8689..f628b8f 100644 --- a/cpython-tests/3.8/test_zipimport.py +++ b/cpython-tests/3.8/test_zipimport.py @@ -10,7 +10,7 @@ from test import support -from zipfile import ZipFile, ZipInfo, ZIP_STORED, ZIP_DEFLATED +from zipfile import ZipInfo, ZIP_STORED, ZIP_DEFLATED import zipimport import linecache @@ -23,6 +23,9 @@ except ImportError: zlib = None +from zipfile2 import ZipFile + + test_src = """\ def get_name(): return __name__ diff --git a/cpython-tests/3.8/test_zipimport_support.py b/cpython-tests/3.8/test_zipimport_support.py deleted file mode 100644 index 8856101..0000000 --- a/cpython-tests/3.8/test_zipimport_support.py +++ /dev/null @@ -1,242 +0,0 @@ -# This test module covers support in various parts of the standard library -# for working with modules located inside zipfiles -# The tests are centralised in this fashion to make it easy to drop them -# if a platform doesn't support zipimport -import test.support -import os -import os.path -import sys -import textwrap -import zipfile -import zipimport -import doctest -import inspect -import linecache -import unittest -from test.support.script_helper import (spawn_python, kill_python, assert_python_ok, - make_script, make_zip_script) - -verbose = test.support.verbose - -# Library modules covered by this test set -# pdb (Issue 4201) -# inspect (Issue 4223) -# doctest (Issue 4197) - -# Other test modules with zipimport related tests -# test_zipimport (of course!) -# test_cmd_line_script (covers the zipimport support in runpy) - -# Retrieve some helpers from other test cases -from test import (test_doctest, sample_doctest, sample_doctest_no_doctests, - sample_doctest_no_docstrings) - - -def _run_object_doctest(obj, module): - finder = doctest.DocTestFinder(verbose=verbose, recurse=False) - runner = doctest.DocTestRunner(verbose=verbose) - # Use the object's fully qualified name if it has one - # Otherwise, use the module's name - try: - name = "%s.%s" % (obj.__module__, obj.__qualname__) - except AttributeError: - name = module.__name__ - for example in finder.find(obj, name, module): - runner.run(example) - f, t = runner.failures, runner.tries - if f: - raise test.support.TestFailed("%d of %d doctests failed" % (f, t)) - if verbose: - print ('doctest (%s) ... %d tests with zero failures' % (module.__name__, t)) - return f, t - - - -class ZipSupportTests(unittest.TestCase): - # This used to use the ImportHooksBaseTestCase to restore - # the state of the import related information - # in the sys module after each test. However, that restores - # *too much* information and breaks for the invocation - # of test_doctest. So we do our own thing and leave - # sys.modules alone. - # We also clear the linecache and zipimport cache - # just to avoid any bogus errors due to name reuse in the tests - def setUp(self): - linecache.clearcache() - zipimport._zip_directory_cache.clear() - self.path = sys.path[:] - self.meta_path = sys.meta_path[:] - self.path_hooks = sys.path_hooks[:] - sys.path_importer_cache.clear() - - def tearDown(self): - sys.path[:] = self.path - sys.meta_path[:] = self.meta_path - sys.path_hooks[:] = self.path_hooks - sys.path_importer_cache.clear() - - def test_inspect_getsource_issue4223(self): - test_src = "def foo(): pass\n" - with test.support.temp_dir() as d: - init_name = make_script(d, '__init__', test_src) - name_in_zip = os.path.join('zip_pkg', - os.path.basename(init_name)) - zip_name, run_name = make_zip_script(d, 'test_zip', - init_name, name_in_zip) - os.remove(init_name) - sys.path.insert(0, zip_name) - import zip_pkg - try: - self.assertEqual(inspect.getsource(zip_pkg.foo), test_src) - finally: - del sys.modules["zip_pkg"] - - def test_doctest_issue4197(self): - # To avoid having to keep two copies of the doctest module's - # unit tests in sync, this test works by taking the source of - # test_doctest itself, rewriting it a bit to cope with a new - # location, and then throwing it in a zip file to make sure - # everything still works correctly - test_src = inspect.getsource(test_doctest) - test_src = test_src.replace( - "from test import test_doctest", - "import test_zipped_doctest as test_doctest") - test_src = test_src.replace("test.test_doctest", - "test_zipped_doctest") - test_src = test_src.replace("test.sample_doctest", - "sample_zipped_doctest") - # The sample doctest files rewritten to include in the zipped version. - sample_sources = {} - for mod in [sample_doctest, sample_doctest_no_doctests, - sample_doctest_no_docstrings]: - src = inspect.getsource(mod) - src = src.replace("test.test_doctest", "test_zipped_doctest") - # Rewrite the module name so that, for example, - # "test.sample_doctest" becomes "sample_zipped_doctest". - mod_name = mod.__name__.split(".")[-1] - mod_name = mod_name.replace("sample_", "sample_zipped_") - sample_sources[mod_name] = src - - with test.support.temp_dir() as d: - script_name = make_script(d, 'test_zipped_doctest', - test_src) - zip_name, run_name = make_zip_script(d, 'test_zip', - script_name) - with zipfile.ZipFile(zip_name, 'a') as z: - for mod_name, src in sample_sources.items(): - z.writestr(mod_name + ".py", src) - if verbose: - with zipfile.ZipFile(zip_name, 'r') as zip_file: - print ('Contents of %r:' % zip_name) - zip_file.printdir() - os.remove(script_name) - sys.path.insert(0, zip_name) - import test_zipped_doctest - try: - # Some of the doc tests depend on the colocated text files - # which aren't available to the zipped version (the doctest - # module currently requires real filenames for non-embedded - # tests). So we're forced to be selective about which tests - # to run. - # doctest could really use some APIs which take a text - # string or a file object instead of a filename... - known_good_tests = [ - test_zipped_doctest.SampleClass, - test_zipped_doctest.SampleClass.NestedClass, - test_zipped_doctest.SampleClass.NestedClass.__init__, - test_zipped_doctest.SampleClass.__init__, - test_zipped_doctest.SampleClass.a_classmethod, - test_zipped_doctest.SampleClass.a_property, - test_zipped_doctest.SampleClass.a_staticmethod, - test_zipped_doctest.SampleClass.double, - test_zipped_doctest.SampleClass.get, - test_zipped_doctest.SampleNewStyleClass, - test_zipped_doctest.SampleNewStyleClass.__init__, - test_zipped_doctest.SampleNewStyleClass.double, - test_zipped_doctest.SampleNewStyleClass.get, - test_zipped_doctest.sample_func, - test_zipped_doctest.test_DocTest, - test_zipped_doctest.test_DocTestParser, - test_zipped_doctest.test_DocTestRunner.basics, - test_zipped_doctest.test_DocTestRunner.exceptions, - test_zipped_doctest.test_DocTestRunner.option_directives, - test_zipped_doctest.test_DocTestRunner.optionflags, - test_zipped_doctest.test_DocTestRunner.verbose_flag, - test_zipped_doctest.test_Example, - test_zipped_doctest.test_debug, - test_zipped_doctest.test_testsource, - test_zipped_doctest.test_trailing_space_in_test, - test_zipped_doctest.test_DocTestSuite, - test_zipped_doctest.test_DocTestFinder, - ] - # These tests are the ones which need access - # to the data files, so we don't run them - fail_due_to_missing_data_files = [ - test_zipped_doctest.test_DocFileSuite, - test_zipped_doctest.test_testfile, - test_zipped_doctest.test_unittest_reportflags, - ] - - for obj in known_good_tests: - _run_object_doctest(obj, test_zipped_doctest) - finally: - del sys.modules["test_zipped_doctest"] - - def test_doctest_main_issue4197(self): - test_src = textwrap.dedent("""\ - class Test: - ">>> 'line 2'" - pass - - import doctest - doctest.testmod() - """) - pattern = 'File "%s", line 2, in %s' - with test.support.temp_dir() as d: - script_name = make_script(d, 'script', test_src) - rc, out, err = assert_python_ok(script_name) - expected = pattern % (script_name, "__main__.Test") - if verbose: - print ("Expected line", expected) - print ("Got stdout:") - print (ascii(out)) - self.assertIn(expected.encode('utf-8'), out) - zip_name, run_name = make_zip_script(d, "test_zip", - script_name, '__main__.py') - rc, out, err = assert_python_ok(zip_name) - expected = pattern % (run_name, "__main__.Test") - if verbose: - print ("Expected line", expected) - print ("Got stdout:") - print (ascii(out)) - self.assertIn(expected.encode('utf-8'), out) - - def test_pdb_issue4201(self): - test_src = textwrap.dedent("""\ - def f(): - pass - - import pdb - pdb.Pdb(nosigint=True).runcall(f) - """) - with test.support.temp_dir() as d: - script_name = make_script(d, 'script', test_src) - p = spawn_python(script_name) - p.stdin.write(b'l\n') - data = kill_python(p) - # bdb/pdb applies normcase to its filename before displaying - self.assertIn(os.path.normcase(script_name.encode('utf-8')), data) - zip_name, run_name = make_zip_script(d, "test_zip", - script_name, '__main__.py') - p = spawn_python(zip_name) - p.stdin.write(b'l\n') - data = kill_python(p) - # bdb/pdb applies normcase to its filename before displaying - self.assertIn(os.path.normcase(run_name.encode('utf-8')), data) - - -def tearDownModule(): - test.support.reap_children() - -if __name__ == '__main__': - unittest.main() diff --git a/cpython-tests/3.9/test_zipfile.py b/cpython-tests/3.9/test_zipfile.py index 5809b2c..6531d6d 100644 --- a/cpython-tests/3.9/test_zipfile.py +++ b/cpython-tests/3.9/test_zipfile.py @@ -24,6 +24,8 @@ requires_zlib, requires_bz2, requires_lzma, captured_stdout) +import zipfile2 + TESTFN2 = TESTFN + "2" TESTFNDIR = TESTFN + "d" FIXEDTEST_SIZE = 1000 @@ -59,7 +61,7 @@ def setUp(self): def make_test_archive(self, f, compression, compresslevel=None): kwargs = {'compression': compression, 'compresslevel': compresslevel} # Create the ZIP archive - with zipfile.ZipFile(f, "w", **kwargs) as zipfp: + with zipfile2.ZipFile(f, "w", **kwargs) as zipfp: zipfp.write(TESTFN, "another.name") zipfp.write(TESTFN, TESTFN) zipfp.writestr("strfile", self.data) @@ -71,7 +73,7 @@ def zip_test(self, f, compression, compresslevel=None): self.make_test_archive(f, compression, compresslevel) # Read the ZIP archive - with zipfile.ZipFile(f, "r", compression) as zipfp: + with zipfile2.ZipFile(f, "r", compression) as zipfp: self.assertEqual(zipfp.read(TESTFN), self.data) self.assertEqual(zipfp.read("another.name"), self.data) self.assertEqual(zipfp.read("strfile"), self.data) @@ -129,7 +131,7 @@ def zip_open_test(self, f, compression): self.make_test_archive(f, compression) # Read the ZIP archive - with zipfile.ZipFile(f, "r", compression) as zipfp: + with zipfile2.ZipFile(f, "r", compression) as zipfp: zipdata1 = [] with zipfp.open(TESTFN) as zipopen1: while True: @@ -156,14 +158,14 @@ def test_open(self): def test_open_with_pathlike(self): path = pathlib.Path(TESTFN2) self.zip_open_test(path, self.compression) - with zipfile.ZipFile(path, "r", self.compression) as zipfp: + with zipfile2.ZipFile(path, "r", self.compression) as zipfp: self.assertIsInstance(zipfp.filename, str) def zip_random_open_test(self, f, compression): self.make_test_archive(f, compression) # Read the ZIP archive - with zipfile.ZipFile(f, "r", compression) as zipfp: + with zipfile2.ZipFile(f, "r", compression) as zipfp: zipdata1 = [] with zipfp.open(TESTFN) as zipopen1: while True: @@ -182,7 +184,7 @@ def zip_read1_test(self, f, compression): self.make_test_archive(f, compression) # Read the ZIP archive - with zipfile.ZipFile(f, "r") as zipfp, \ + with zipfile2.ZipFile(f, "r") as zipfp, \ zipfp.open(TESTFN) as zipopen: zipdata = [] while True: @@ -201,7 +203,7 @@ def zip_read1_10_test(self, f, compression): self.make_test_archive(f, compression) # Read the ZIP archive - with zipfile.ZipFile(f, "r") as zipfp, \ + with zipfile2.ZipFile(f, "r") as zipfp, \ zipfp.open(TESTFN) as zipopen: zipdata = [] while True: @@ -221,7 +223,7 @@ def zip_readline_read_test(self, f, compression): self.make_test_archive(f, compression) # Read the ZIP archive - with zipfile.ZipFile(f, "r") as zipfp, \ + with zipfile2.ZipFile(f, "r") as zipfp, \ zipfp.open(TESTFN) as zipopen: data = b'' while True: @@ -246,7 +248,7 @@ def zip_readline_test(self, f, compression): self.make_test_archive(f, compression) # Read the ZIP archive - with zipfile.ZipFile(f, "r") as zipfp: + with zipfile2.ZipFile(f, "r") as zipfp: with zipfp.open(TESTFN) as zipopen: for line in self.line_gen: linedata = zipopen.readline() @@ -260,7 +262,7 @@ def zip_readlines_test(self, f, compression): self.make_test_archive(f, compression) # Read the ZIP archive - with zipfile.ZipFile(f, "r") as zipfp: + with zipfile2.ZipFile(f, "r") as zipfp: with zipfp.open(TESTFN) as zipopen: ziplines = zipopen.readlines() for line, zipline in zip(self.line_gen, ziplines): @@ -274,7 +276,7 @@ def zip_iterlines_test(self, f, compression): self.make_test_archive(f, compression) # Read the ZIP archive - with zipfile.ZipFile(f, "r") as zipfp: + with zipfile2.ZipFile(f, "r") as zipfp: with zipfp.open(TESTFN) as zipopen: for line, zipline in zip(self.line_gen, zipopen): self.assertEqual(zipline, line) @@ -286,23 +288,23 @@ def test_iterlines(self): def test_low_compression(self): """Check for cases where compressed data is larger than original.""" # Create the ZIP archive - with zipfile.ZipFile(TESTFN2, "w", self.compression) as zipfp: + with zipfile2.ZipFile(TESTFN2, "w", self.compression) as zipfp: zipfp.writestr("strfile", '12') # Get an open object for strfile - with zipfile.ZipFile(TESTFN2, "r", self.compression) as zipfp: + with zipfile2.ZipFile(TESTFN2, "r", self.compression) as zipfp: with zipfp.open("strfile") as openobj: self.assertEqual(openobj.read(1), b'1') self.assertEqual(openobj.read(1), b'2') def test_writestr_compression(self): - zipfp = zipfile.ZipFile(TESTFN2, "w") + zipfp = zipfile2.ZipFile(TESTFN2, "w") zipfp.writestr("b.txt", "hello world", compress_type=self.compression) info = zipfp.getinfo('b.txt') self.assertEqual(info.compress_type, self.compression) def test_writestr_compresslevel(self): - zipfp = zipfile.ZipFile(TESTFN2, "w", compresslevel=1) + zipfp = zipfile2.ZipFile(TESTFN2, "w", compresslevel=1) zipfp.writestr("a.txt", "hello world", compress_type=self.compression) zipfp.writestr("b.txt", "hello world", compress_type=self.compression, compresslevel=2) @@ -323,7 +325,7 @@ def test_read_return_size(self): for test_size in (1, 4095, 4096, 4097, 16384): file_size = test_size + 1 junk = randbytes(file_size) - with zipfile.ZipFile(io.BytesIO(), "w", self.compression) as zipf: + with zipfile2.ZipFile(io.BytesIO(), "w", self.compression) as zipf: zipf.writestr('foo', junk) with zipf.open('foo', 'r') as fp: buf = fp.read(test_size) @@ -331,20 +333,20 @@ def test_read_return_size(self): def test_truncated_zipfile(self): fp = io.BytesIO() - with zipfile.ZipFile(fp, mode='w') as zipf: + with zipfile2.ZipFile(fp, mode='w') as zipf: zipf.writestr('strfile', self.data, compress_type=self.compression) end_offset = fp.tell() zipfiledata = fp.getvalue() fp = io.BytesIO(zipfiledata) - with zipfile.ZipFile(fp) as zipf: + with zipfile2.ZipFile(fp) as zipf: with zipf.open('strfile') as zipopen: fp.truncate(end_offset - 20) with self.assertRaises(EOFError): zipopen.read() fp = io.BytesIO(zipfiledata) - with zipfile.ZipFile(fp) as zipf: + with zipfile2.ZipFile(fp) as zipf: with zipf.open('strfile') as zipopen: fp.truncate(end_offset - 20) with self.assertRaises(EOFError): @@ -352,7 +354,7 @@ def test_truncated_zipfile(self): pass fp = io.BytesIO(zipfiledata) - with zipfile.ZipFile(fp) as zipf: + with zipfile2.ZipFile(fp) as zipf: with zipf.open('strfile') as zipopen: fp.truncate(end_offset - 20) with self.assertRaises(EOFError): @@ -362,12 +364,12 @@ def test_truncated_zipfile(self): def test_repr(self): fname = 'file.name' for f in get_files(self): - with zipfile.ZipFile(f, 'w', self.compression) as zipfp: + with zipfile2.ZipFile(f, 'w', self.compression) as zipfp: zipfp.write(TESTFN, fname) r = repr(zipfp) self.assertIn("mode='w'", r) - with zipfile.ZipFile(f, 'r') as zipfp: + with zipfile2.ZipFile(f, 'r') as zipfp: r = repr(zipfp) if isinstance(f, str): self.assertIn('filename=%r' % f, r) @@ -397,7 +399,7 @@ def test_compresslevel_basic(self): def test_per_file_compresslevel(self): """Check that files within a Zip archive can have different compression levels.""" - with zipfile.ZipFile(TESTFN2, "w", compresslevel=1) as zipfp: + with zipfile2.ZipFile(TESTFN2, "w", compresslevel=1) as zipfp: zipfp.write(TESTFN, 'compress_1') zipfp.write(TESTFN, 'compress_9', compresslevel=9) one_info = zipfp.getinfo('compress_1') @@ -419,7 +421,7 @@ def write(self, data): while True: testfile = BrokenFile() count = None - with zipfile.ZipFile(testfile, 'w', self.compression) as zipfp: + with zipfile2.ZipFile(testfile, 'w', self.compression) as zipfp: with zipfp.open('file1', 'w') as f: f.write(b'data1') count = 0 @@ -432,11 +434,11 @@ def write(self, data): break finally: count = None - with zipfile.ZipFile(io.BytesIO(testfile.getvalue())) as zipfp: + with zipfile2.ZipFile(io.BytesIO(testfile.getvalue())) as zipfp: self.assertEqual(zipfp.namelist(), ['file1']) self.assertEqual(zipfp.read('file1'), b'data1') - with zipfile.ZipFile(io.BytesIO(testfile.getvalue())) as zipfp: + with zipfile2.ZipFile(io.BytesIO(testfile.getvalue())) as zipfp: self.assertEqual(zipfp.namelist(), ['file1', 'file2']) self.assertEqual(zipfp.read('file1'), b'data1') self.assertEqual(zipfp.read('file2'), b'data2') @@ -458,7 +460,7 @@ def zip_test_writestr_permissions(self, f, compression): # instance. self.make_test_archive(f, compression) - with zipfile.ZipFile(f, "r") as zipfp: + with zipfile2.ZipFile(f, "r") as zipfp: zinfo = zipfp.getinfo('strfile') self.assertEqual(zinfo.external_attr, 0o600 << 16) @@ -470,18 +472,18 @@ def test_writestr_permissions(self): self.zip_test_writestr_permissions(f, zipfile.ZIP_STORED) def test_absolute_arcnames(self): - with zipfile.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED) as zipfp: + with zipfile2.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED) as zipfp: zipfp.write(TESTFN, "/absolute") - with zipfile.ZipFile(TESTFN2, "r", zipfile.ZIP_STORED) as zipfp: + with zipfile2.ZipFile(TESTFN2, "r", zipfile.ZIP_STORED) as zipfp: self.assertEqual(zipfp.namelist(), ["absolute"]) def test_append_to_zip_file(self): """Test appending to an existing zipfile.""" - with zipfile.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED) as zipfp: + with zipfile2.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED) as zipfp: zipfp.write(TESTFN, TESTFN) - with zipfile.ZipFile(TESTFN2, "a", zipfile.ZIP_STORED) as zipfp: + with zipfile2.ZipFile(TESTFN2, "a", zipfile.ZIP_STORED) as zipfp: zipfp.writestr("strfile", self.data) self.assertEqual(zipfp.namelist(), [TESTFN, "strfile"]) @@ -493,24 +495,24 @@ def test_append_to_non_zip_file(self): with open(TESTFN2, 'wb') as f: f.write(data) - with zipfile.ZipFile(TESTFN2, "a", zipfile.ZIP_STORED) as zipfp: + with zipfile2.ZipFile(TESTFN2, "a", zipfile.ZIP_STORED) as zipfp: zipfp.write(TESTFN, TESTFN) with open(TESTFN2, 'rb') as f: f.seek(len(data)) - with zipfile.ZipFile(f, "r") as zipfp: + with zipfile2.ZipFile(f, "r") as zipfp: self.assertEqual(zipfp.namelist(), [TESTFN]) self.assertEqual(zipfp.read(TESTFN), self.data) with open(TESTFN2, 'rb') as f: self.assertEqual(f.read(len(data)), data) zipfiledata = f.read() - with io.BytesIO(zipfiledata) as bio, zipfile.ZipFile(bio) as zipfp: + with io.BytesIO(zipfiledata) as bio, zipfile2.ZipFile(bio) as zipfp: self.assertEqual(zipfp.namelist(), [TESTFN]) self.assertEqual(zipfp.read(TESTFN), self.data) def test_read_concatenated_zip_file(self): with io.BytesIO() as bio: - with zipfile.ZipFile(bio, 'w', zipfile.ZIP_STORED) as zipfp: + with zipfile2.ZipFile(bio, 'w', zipfile.ZIP_STORED) as zipfp: zipfp.write(TESTFN, TESTFN) zipfiledata = bio.getvalue() data = b'I am not a ZipFile!'*10 @@ -518,13 +520,13 @@ def test_read_concatenated_zip_file(self): f.write(data) f.write(zipfiledata) - with zipfile.ZipFile(TESTFN2) as zipfp: + with zipfile2.ZipFile(TESTFN2) as zipfp: self.assertEqual(zipfp.namelist(), [TESTFN]) self.assertEqual(zipfp.read(TESTFN), self.data) def test_append_to_concatenated_zip_file(self): with io.BytesIO() as bio: - with zipfile.ZipFile(bio, 'w', zipfile.ZIP_STORED) as zipfp: + with zipfile2.ZipFile(bio, 'w', zipfile.ZIP_STORED) as zipfp: zipfp.write(TESTFN, TESTFN) zipfiledata = bio.getvalue() data = b'I am not a ZipFile!'*1000000 @@ -532,50 +534,50 @@ def test_append_to_concatenated_zip_file(self): f.write(data) f.write(zipfiledata) - with zipfile.ZipFile(TESTFN2, 'a') as zipfp: + with zipfile2.ZipFile(TESTFN2, 'a') as zipfp: self.assertEqual(zipfp.namelist(), [TESTFN]) zipfp.writestr('strfile', self.data) with open(TESTFN2, 'rb') as f: self.assertEqual(f.read(len(data)), data) zipfiledata = f.read() - with io.BytesIO(zipfiledata) as bio, zipfile.ZipFile(bio) as zipfp: + with io.BytesIO(zipfiledata) as bio, zipfile2.ZipFile(bio) as zipfp: self.assertEqual(zipfp.namelist(), [TESTFN, 'strfile']) self.assertEqual(zipfp.read(TESTFN), self.data) self.assertEqual(zipfp.read('strfile'), self.data) def test_ignores_newline_at_end(self): - with zipfile.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED) as zipfp: + with zipfile2.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED) as zipfp: zipfp.write(TESTFN, TESTFN) with open(TESTFN2, 'a') as f: f.write("\r\n\00\00\00") - with zipfile.ZipFile(TESTFN2, "r") as zipfp: - self.assertIsInstance(zipfp, zipfile.ZipFile) + with zipfile2.ZipFile(TESTFN2, "r") as zipfp: + self.assertIsInstance(zipfp, zipfile2.ZipFile) def test_ignores_stuff_appended_past_comments(self): - with zipfile.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED) as zipfp: + with zipfile2.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED) as zipfp: zipfp.comment = b"this is a comment" zipfp.write(TESTFN, TESTFN) with open(TESTFN2, 'a') as f: f.write("abcdef\r\n") - with zipfile.ZipFile(TESTFN2, "r") as zipfp: - self.assertIsInstance(zipfp, zipfile.ZipFile) + with zipfile2.ZipFile(TESTFN2, "r") as zipfp: + self.assertIsInstance(zipfp, zipfile2.ZipFile) self.assertEqual(zipfp.comment, b"this is a comment") def test_write_default_name(self): """Check that calling ZipFile.write without arcname specified produces the expected result.""" - with zipfile.ZipFile(TESTFN2, "w") as zipfp: + with zipfile2.ZipFile(TESTFN2, "w") as zipfp: zipfp.write(TESTFN) with open(TESTFN, "rb") as f: self.assertEqual(zipfp.read(TESTFN), f.read()) def test_io_on_closed_zipextfile(self): fname = "somefile.txt" - with zipfile.ZipFile(TESTFN2, mode="w") as zipfp: + with zipfile2.ZipFile(TESTFN2, mode="w") as zipfp: zipfp.writestr(fname, "bogus") - with zipfile.ZipFile(TESTFN2, mode="r") as zipfp: + with zipfile2.ZipFile(TESTFN2, mode="r") as zipfp: with zipfp.open(fname) as fid: fid.close() self.assertRaises(ValueError, fid.read) @@ -587,23 +589,23 @@ def test_io_on_closed_zipextfile(self): def test_write_to_readonly(self): """Check that trying to call write() on a readonly ZipFile object raises a ValueError.""" - with zipfile.ZipFile(TESTFN2, mode="w") as zipfp: + with zipfile2.ZipFile(TESTFN2, mode="w") as zipfp: zipfp.writestr("somefile.txt", "bogus") - with zipfile.ZipFile(TESTFN2, mode="r") as zipfp: + with zipfile2.ZipFile(TESTFN2, mode="r") as zipfp: self.assertRaises(ValueError, zipfp.write, TESTFN) - with zipfile.ZipFile(TESTFN2, mode="r") as zipfp: + with zipfile2.ZipFile(TESTFN2, mode="r") as zipfp: with self.assertRaises(ValueError): zipfp.open(TESTFN, mode='w') def test_add_file_before_1980(self): # Set atime and mtime to 1970-01-01 os.utime(TESTFN, (0, 0)) - with zipfile.ZipFile(TESTFN2, "w") as zipfp: + with zipfile2.ZipFile(TESTFN2, "w") as zipfp: self.assertRaises(ValueError, zipfp.write, TESTFN) - with zipfile.ZipFile(TESTFN2, "w", strict_timestamps=False) as zipfp: + with zipfile2.ZipFile(TESTFN2, "w", strict_timestamps=False) as zipfp: zipfp.write(TESTFN) zinfo = zipfp.getinfo(TESTFN) self.assertEqual(zinfo.date_time, (1980, 1, 1, 0, 0, 0)) @@ -632,10 +634,10 @@ def test_add_file_after_2107(self): # https://bugs.python.org/issue39460#msg360952 self.skipTest(f"Linux VFS/XFS kernel bug detected: {mtime_ns=}") - with zipfile.ZipFile(TESTFN2, "w") as zipfp: + with zipfile2.ZipFile(TESTFN2, "w") as zipfp: self.assertRaises(struct.error, zipfp.write, TESTFN) - with zipfile.ZipFile(TESTFN2, "w", strict_timestamps=False) as zipfp: + with zipfile2.ZipFile(TESTFN2, "w", strict_timestamps=False) as zipfp: zipfp.write(TESTFN) zinfo = zipfp.getinfo(TESTFN) self.assertEqual(zinfo.date_time, (2107, 12, 31, 23, 59, 59)) @@ -649,7 +651,7 @@ class DeflateTestsWithSourceFile(AbstractTestsWithSourceFile, def test_per_file_compression(self): """Check that files within a Zip archive can have different compression options.""" - with zipfile.ZipFile(TESTFN2, "w") as zipfp: + with zipfile2.ZipFile(TESTFN2, "w") as zipfp: zipfp.write(TESTFN, 'storeme', zipfile.ZIP_STORED) zipfp.write(TESTFN, 'deflateme', zipfile.ZIP_DEFLATED) sinfo = zipfp.getinfo('storeme') @@ -690,13 +692,13 @@ def setUp(self): def zip_test(self, f, compression): # Create the ZIP archive - with zipfile.ZipFile(f, "w", compression, allowZip64=True) as zipfp: + with zipfile2.ZipFile(f, "w", compression, allowZip64=True) as zipfp: zipfp.write(TESTFN, "another.name") zipfp.write(TESTFN, TESTFN) zipfp.writestr("strfile", self.data) # Read the ZIP archive - with zipfile.ZipFile(f, "r", compression) as zipfp: + with zipfile2.ZipFile(f, "r", compression) as zipfp: self.assertEqual(zipfp.read(TESTFN), self.data) self.assertEqual(zipfp.read("another.name"), self.data) self.assertEqual(zipfp.read("strfile"), self.data) @@ -752,7 +754,7 @@ def test_basic(self): def test_too_many_files(self): # This test checks that more than 64k files can be added to an archive, # and that the resulting archive can be read properly by ZipFile - zipf = zipfile.ZipFile(TESTFN, "w", self.compression, + zipf = zipfile2.ZipFile(TESTFN, "w", self.compression, allowZip64=True) zipf.debug = 100 numfiles = 15 @@ -761,7 +763,7 @@ def test_too_many_files(self): self.assertEqual(len(zipf.namelist()), numfiles) zipf.close() - zipf2 = zipfile.ZipFile(TESTFN, "r", self.compression) + zipf2 = zipfile2.ZipFile(TESTFN, "r", self.compression) self.assertEqual(len(zipf2.namelist()), numfiles) for i in range(numfiles): content = zipf2.read("foo%08d" % i).decode('ascii') @@ -769,7 +771,7 @@ def test_too_many_files(self): zipf2.close() def test_too_many_files_append(self): - zipf = zipfile.ZipFile(TESTFN, "w", self.compression, + zipf = zipfile2.ZipFile(TESTFN, "w", self.compression, allowZip64=False) zipf.debug = 100 numfiles = 9 @@ -781,7 +783,7 @@ def test_too_many_files_append(self): self.assertEqual(len(zipf.namelist()), numfiles) zipf.close() - zipf = zipfile.ZipFile(TESTFN, "a", self.compression, + zipf = zipfile2.ZipFile(TESTFN, "a", self.compression, allowZip64=False) zipf.debug = 100 self.assertEqual(len(zipf.namelist()), numfiles) @@ -790,7 +792,7 @@ def test_too_many_files_append(self): self.assertEqual(len(zipf.namelist()), numfiles) zipf.close() - zipf = zipfile.ZipFile(TESTFN, "a", self.compression, + zipf = zipfile2.ZipFile(TESTFN, "a", self.compression, allowZip64=True) zipf.debug = 100 self.assertEqual(len(zipf.namelist()), numfiles) @@ -800,7 +802,7 @@ def test_too_many_files_append(self): self.assertEqual(len(zipf.namelist()), numfiles2) zipf.close() - zipf2 = zipfile.ZipFile(TESTFN, "r", self.compression) + zipf2 = zipfile2.ZipFile(TESTFN, "r", self.compression) self.assertEqual(len(zipf2.namelist()), numfiles2) for i in range(numfiles2): content = zipf2.read("foo%08d" % i).decode('ascii') @@ -819,12 +821,12 @@ class StoredTestZip64InSmallFiles(AbstractTestZip64InSmallFiles, compression = zipfile.ZIP_STORED def large_file_exception_test(self, f, compression): - with zipfile.ZipFile(f, "w", compression, allowZip64=False) as zipfp: + with zipfile2.ZipFile(f, "w", compression, allowZip64=False) as zipfp: self.assertRaises(zipfile.LargeZipFile, zipfp.write, TESTFN, "another.name") def large_file_exception_test2(self, f, compression): - with zipfile.ZipFile(f, "w", compression, allowZip64=False) as zipfp: + with zipfile2.ZipFile(f, "w", compression, allowZip64=False) as zipfp: self.assertRaises(zipfile.LargeZipFile, zipfp.writestr, "another.name", self.data) @@ -834,24 +836,24 @@ def test_large_file_exception(self): self.large_file_exception_test2(f, zipfile.ZIP_STORED) def test_absolute_arcnames(self): - with zipfile.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED, + with zipfile2.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED, allowZip64=True) as zipfp: zipfp.write(TESTFN, "/absolute") - with zipfile.ZipFile(TESTFN2, "r", zipfile.ZIP_STORED) as zipfp: + with zipfile2.ZipFile(TESTFN2, "r", zipfile.ZIP_STORED) as zipfp: self.assertEqual(zipfp.namelist(), ["absolute"]) def test_append(self): # Test that appending to the Zip64 archive doesn't change # extra fields of existing entries. - with zipfile.ZipFile(TESTFN2, "w", allowZip64=True) as zipfp: + with zipfile2.ZipFile(TESTFN2, "w", allowZip64=True) as zipfp: zipfp.writestr("strfile", self.data) - with zipfile.ZipFile(TESTFN2, "r", allowZip64=True) as zipfp: + with zipfile2.ZipFile(TESTFN2, "r", allowZip64=True) as zipfp: zinfo = zipfp.getinfo("strfile") extra = zinfo.extra - with zipfile.ZipFile(TESTFN2, "a", allowZip64=True) as zipfp: + with zipfile2.ZipFile(TESTFN2, "a", allowZip64=True) as zipfp: zipfp.writestr("strfile2", self.data) - with zipfile.ZipFile(TESTFN2, "r", allowZip64=True) as zipfp: + with zipfile2.ZipFile(TESTFN2, "r", allowZip64=True) as zipfp: zinfo = zipfp.getinfo("strfile") self.assertEqual(zinfo.extra, extra) @@ -976,7 +978,7 @@ def test_bad_zip64_extra(self): file_size_64_set=True, ) with self.assertRaises(zipfile.BadZipFile) as e: - zipfile.ZipFile(io.BytesIO(missing_file_size_extra)) + zipfile2.ZipFile(io.BytesIO(missing_file_size_extra)) self.assertIn('file size', str(e.exception).lower()) # zip64 file size present, zip64 compress size present, one field in @@ -987,7 +989,7 @@ def test_bad_zip64_extra(self): compress_size_64_set=True, ) with self.assertRaises(zipfile.BadZipFile) as e: - zipfile.ZipFile(io.BytesIO(missing_compress_size_extra)) + zipfile2.ZipFile(io.BytesIO(missing_compress_size_extra)) self.assertIn('compress size', str(e.exception).lower()) # zip64 compress size present, no fields in extra, expecting one, @@ -996,7 +998,7 @@ def test_bad_zip64_extra(self): compress_size_64_set=True, ) with self.assertRaises(zipfile.BadZipFile) as e: - zipfile.ZipFile(io.BytesIO(missing_compress_size_extra)) + zipfile2.ZipFile(io.BytesIO(missing_compress_size_extra)) self.assertIn('compress size', str(e.exception).lower()) # zip64 file size present, zip64 compress size present, zip64 header @@ -1010,7 +1012,7 @@ def test_bad_zip64_extra(self): header_offset_64_set=True, ) with self.assertRaises(zipfile.BadZipFile) as e: - zipfile.ZipFile(io.BytesIO(missing_header_offset_extra)) + zipfile2.ZipFile(io.BytesIO(missing_header_offset_extra)) self.assertIn('header offset', str(e.exception).lower()) # zip64 compress size present, zip64 header offset present, one field @@ -1022,7 +1024,7 @@ def test_bad_zip64_extra(self): header_offset_64_set=True, ) with self.assertRaises(zipfile.BadZipFile) as e: - zipfile.ZipFile(io.BytesIO(missing_header_offset_extra)) + zipfile2.ZipFile(io.BytesIO(missing_header_offset_extra)) self.assertIn('header offset', str(e.exception).lower()) # zip64 file size present, zip64 header offset present, one field in @@ -1034,7 +1036,7 @@ def test_bad_zip64_extra(self): header_offset_64_set=True, ) with self.assertRaises(zipfile.BadZipFile) as e: - zipfile.ZipFile(io.BytesIO(missing_header_offset_extra)) + zipfile2.ZipFile(io.BytesIO(missing_header_offset_extra)) self.assertIn('header offset', str(e.exception).lower()) # zip64 header offset present, no fields in extra, expecting one, @@ -1045,7 +1047,7 @@ def test_bad_zip64_extra(self): header_offset_64_set=True, ) with self.assertRaises(zipfile.BadZipFile) as e: - zipfile.ZipFile(io.BytesIO(missing_header_offset_extra)) + zipfile2.ZipFile(io.BytesIO(missing_header_offset_extra)) self.assertIn('header offset', str(e.exception).lower()) def test_generated_valid_zip64_extra(self): @@ -1068,7 +1070,7 @@ def test_generated_valid_zip64_extra(self): kwargs = {} for c in combo: kwargs.update(c) - with zipfile.ZipFile(io.BytesIO(self.make_zip64_file(**kwargs))) as zf: + with zipfile2.ZipFile(io.BytesIO(self.make_zip64_file(**kwargs))) as zf: zinfo = zf.infolist()[0] self.assertEqual(zinfo.file_size, expected_file_size) self.assertEqual(zinfo.compress_size, expected_compress_size) @@ -1099,7 +1101,7 @@ def tearDown(self): def test_close_after_close(self): data = b'content' - with zipfile.ZipFile(TESTFN2, "w", self.compression) as zipf: + with zipfile2.ZipFile(TESTFN2, "w", self.compression) as zipf: w = zipf.open('test', 'w') w.write(data) w.close() @@ -1110,7 +1112,7 @@ def test_close_after_close(self): def test_write_after_close(self): data = b'content' - with zipfile.ZipFile(TESTFN2, "w", self.compression) as zipf: + with zipfile2.ZipFile(TESTFN2, "w", self.compression) as zipf: w = zipf.open('test', 'w') w.write(data) w.close() @@ -1121,7 +1123,7 @@ def test_write_after_close(self): def test_issue44439(self): q = array.array('Q', [1, 2, 3, 4, 5]) LENGTH = len(q) * q.itemsize - with zipfile.ZipFile(io.BytesIO(), 'w', self.compression) as zip: + with zipfile2.ZipFile(io.BytesIO(), 'w', self.compression) as zip: with zip.open('data', 'w') as data: self.assertEqual(data.write(q), LENGTH) self.assertEqual(zip.getinfo('data').file_size, LENGTH) @@ -1338,14 +1340,14 @@ def test_write_pathlike(self): class ExtractTests(unittest.TestCase): def make_test_file(self): - with zipfile.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED) as zipfp: + with zipfile2.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED) as zipfp: for fpath, fdata in SMALL_TEST_DATA: zipfp.writestr(fpath, fdata) def test_extract(self): with temp_cwd(): self.make_test_file() - with zipfile.ZipFile(TESTFN2, "r") as zipfp: + with zipfile2.ZipFile(TESTFN2, "r") as zipfp: for fpath, fdata in SMALL_TEST_DATA: writtenfile = zipfp.extract(fpath) @@ -1363,7 +1365,7 @@ def test_extract(self): def _test_extract_with_target(self, target): self.make_test_file() - with zipfile.ZipFile(TESTFN2, "r") as zipfp: + with zipfile2.ZipFile(TESTFN2, "r") as zipfp: for fpath, fdata in SMALL_TEST_DATA: writtenfile = zipfp.extract(fpath, target) @@ -1391,7 +1393,7 @@ def test_extract_with_target_pathlike(self): def test_extract_all(self): with temp_cwd(): self.make_test_file() - with zipfile.ZipFile(TESTFN2, "r") as zipfp: + with zipfile2.ZipFile(TESTFN2, "r") as zipfp: zipfp.extractall() for fpath, fdata in SMALL_TEST_DATA: outfile = os.path.join(os.getcwd(), fpath) @@ -1403,7 +1405,7 @@ def test_extract_all(self): def _test_extract_all_with_target(self, target): self.make_test_file() - with zipfile.ZipFile(TESTFN2, "r") as zipfp: + with zipfile2.ZipFile(TESTFN2, "r") as zipfp: zipfp.extractall(target) for fpath, fdata in SMALL_TEST_DATA: outfile = os.path.join(target, fpath) @@ -1429,7 +1431,7 @@ def check_file(self, filename, content): self.assertEqual(f.read(), content) def test_sanitize_windows_name(self): - san = zipfile.ZipFile._sanitize_windows_name + san = zipfile2.ZipFile._sanitize_windows_name # Passing pathsep in allows this test to work regardless of platform. self.assertEqual(san(r',,?,C:,foo,bar/z', ','), r'_,C_,foo,bar/z') self.assertEqual(san(r'a\b,ce|f"g?h*i', ','), r'a\b,c_d_e_f_g_h_i') @@ -1486,7 +1488,7 @@ def test_extract_hackers_arcnames_posix_only(self): def _test_extract_hackers_arcnames(self, hacknames): for arcname, fixedname in hacknames: content = b'foobar' + arcname.encode() - with zipfile.ZipFile(TESTFN2, 'w', zipfile.ZIP_STORED) as zipfp: + with zipfile2.ZipFile(TESTFN2, 'w', zipfile.ZIP_STORED) as zipfp: zinfo = zipfile.ZipInfo() # preserve backslashes zinfo.filename = arcname @@ -1497,7 +1499,7 @@ def _test_extract_hackers_arcnames(self, hacknames): targetpath = os.path.join('target', 'subdir', 'subsub') correctfile = os.path.join(targetpath, *fixedname.split('/')) - with zipfile.ZipFile(TESTFN2, 'r') as zipfp: + with zipfile2.ZipFile(TESTFN2, 'r') as zipfp: writtenfile = zipfp.extract(arcname, targetpath) self.assertEqual(writtenfile, correctfile, msg='extract %r: %r != %r' % @@ -1505,21 +1507,21 @@ def _test_extract_hackers_arcnames(self, hacknames): self.check_file(correctfile, content) rmtree('target') - with zipfile.ZipFile(TESTFN2, 'r') as zipfp: + with zipfile2.ZipFile(TESTFN2, 'r') as zipfp: zipfp.extractall(targetpath) self.check_file(correctfile, content) rmtree('target') correctfile = os.path.join(os.getcwd(), *fixedname.split('/')) - with zipfile.ZipFile(TESTFN2, 'r') as zipfp: + with zipfile2.ZipFile(TESTFN2, 'r') as zipfp: writtenfile = zipfp.extract(arcname) self.assertEqual(writtenfile, correctfile, msg="extract %r" % arcname) self.check_file(correctfile, content) rmtree(fixedname.split('/')[0]) - with zipfile.ZipFile(TESTFN2, 'r') as zipfp: + with zipfile2.ZipFile(TESTFN2, 'r') as zipfp: zipfp.extractall() self.check_file(correctfile, content) rmtree(fixedname.split('/')[0]) @@ -1530,13 +1532,13 @@ def _test_extract_hackers_arcnames(self, hacknames): class OtherTests(unittest.TestCase): def test_open_via_zip_info(self): # Create the ZIP archive - with zipfile.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED) as zipfp: + with zipfile2.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED) as zipfp: zipfp.writestr("name", "foo") with self.assertWarns(UserWarning): zipfp.writestr("name", "bar") self.assertEqual(zipfp.namelist(), ["name"] * 2) - with zipfile.ZipFile(TESTFN2, "r") as zipfp: + with zipfile2.ZipFile(TESTFN2, "r") as zipfp: infos = zipfp.infolist() data = b"" for info in infos: @@ -1549,7 +1551,7 @@ def test_open_via_zip_info(self): self.assertIn(data, {b"foobar", b"barfoo"}) def test_writestr_extended_local_header_issue1202(self): - with zipfile.ZipFile(TESTFN2, 'w') as orig_zip: + with zipfile2.ZipFile(TESTFN2, 'w') as orig_zip: for data in 'abcdefghijklmnop': zinfo = zipfile.ZipInfo(data) zinfo.flag_bits |= 0x08 # Include an extended local header. @@ -1557,25 +1559,25 @@ def test_writestr_extended_local_header_issue1202(self): def test_close(self): """Check that the zipfile is closed after the 'with' block.""" - with zipfile.ZipFile(TESTFN2, "w") as zipfp: + with zipfile2.ZipFile(TESTFN2, "w") as zipfp: for fpath, fdata in SMALL_TEST_DATA: zipfp.writestr(fpath, fdata) self.assertIsNotNone(zipfp.fp, 'zipfp is not open') self.assertIsNone(zipfp.fp, 'zipfp is not closed') - with zipfile.ZipFile(TESTFN2, "r") as zipfp: + with zipfile2.ZipFile(TESTFN2, "r") as zipfp: self.assertIsNotNone(zipfp.fp, 'zipfp is not open') self.assertIsNone(zipfp.fp, 'zipfp is not closed') def test_close_on_exception(self): """Check that the zipfile is closed if an exception is raised in the 'with' block.""" - with zipfile.ZipFile(TESTFN2, "w") as zipfp: + with zipfile2.ZipFile(TESTFN2, "w") as zipfp: for fpath, fdata in SMALL_TEST_DATA: zipfp.writestr(fpath, fdata) try: - with zipfile.ZipFile(TESTFN2, "r") as zipfp2: + with zipfile2.ZipFile(TESTFN2, "r") as zipfp2: raise zipfile.BadZipFile() except zipfile.BadZipFile: self.assertIsNone(zipfp2.fp, 'zipfp is not closed') @@ -1588,29 +1590,29 @@ def test_unsupported_version(self): b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x01\x00\x00\x00\x00xPK\x05\x06' b'\x00\x00\x00\x00\x01\x00\x01\x00/\x00\x00\x00\x1f\x00\x00\x00\x00\x00') - self.assertRaises(NotImplementedError, zipfile.ZipFile, + self.assertRaises(NotImplementedError, zipfile2.ZipFile, io.BytesIO(data), 'r') @requires_zlib() def test_read_unicode_filenames(self): # bug #10801 fname = findfile('zip_cp437_header.zip') - with zipfile.ZipFile(fname) as zipfp: + with zipfile2.ZipFile(fname) as zipfp: for name in zipfp.namelist(): zipfp.open(name).close() def test_write_unicode_filenames(self): - with zipfile.ZipFile(TESTFN, "w") as zf: + with zipfile2.ZipFile(TESTFN, "w") as zf: zf.writestr("foo.txt", "Test for unicode filename") zf.writestr("\xf6.txt", "Test for unicode filename") self.assertIsInstance(zf.infolist()[0].filename, str) - with zipfile.ZipFile(TESTFN, "r") as zf: + with zipfile2.ZipFile(TESTFN, "r") as zf: self.assertEqual(zf.filelist[0].filename, "foo.txt") self.assertEqual(zf.filelist[1].filename, "\xf6.txt") def test_read_after_write_unicode_filenames(self): - with zipfile.ZipFile(TESTFN2, 'w') as zipfp: + with zipfile2.ZipFile(TESTFN2, 'w') as zipfp: zipfp.writestr('приклад', b'sample') self.assertEqual(zipfp.read('приклад'), b'sample') @@ -1619,11 +1621,11 @@ def test_exclusive_create_zip_file(self): unlink(TESTFN2) filename = 'testfile.txt' content = b'hello, world. this is some content.' - with zipfile.ZipFile(TESTFN2, "x", zipfile.ZIP_STORED) as zipfp: + with zipfile2.ZipFile(TESTFN2, "x", zipfile.ZIP_STORED) as zipfp: zipfp.writestr(filename, content) with self.assertRaises(FileExistsError): - zipfile.ZipFile(TESTFN2, "x", zipfile.ZIP_STORED) - with zipfile.ZipFile(TESTFN2, "r") as zipfp: + zipfile2.ZipFile(TESTFN2, "x", zipfile.ZIP_STORED) + with zipfile2.ZipFile(TESTFN2, "r") as zipfp: self.assertEqual(zipfp.namelist(), [filename]) self.assertEqual(zipfp.read(filename), content) @@ -1635,14 +1637,14 @@ def test_create_non_existent_file_for_append(self): content = b'hello, world. this is some content.' try: - with zipfile.ZipFile(TESTFN, 'a') as zf: + with zipfile2.ZipFile(TESTFN, 'a') as zf: zf.writestr(filename, content) except OSError: self.fail('Could not append data to a non-existent zip file.') self.assertTrue(os.path.exists(TESTFN)) - with zipfile.ZipFile(TESTFN, 'r') as zf: + with zipfile2.ZipFile(TESTFN, 'r') as zf: self.assertEqual(zf.read(filename), content) def test_close_erroneous_file(self): @@ -1656,7 +1658,7 @@ def test_close_erroneous_file(self): with open(TESTFN, "w") as fp: fp.write("this is not a legal zip file\n") try: - zf = zipfile.ZipFile(TESTFN) + zf = zipfile2.ZipFile(TESTFN) except zipfile.BadZipFile: pass @@ -1682,7 +1684,7 @@ def test_damaged_zipfile(self): """Check that zipfiles with missing bytes at the end raise BadZipFile.""" # - Create a valid zip file fp = io.BytesIO() - with zipfile.ZipFile(fp, mode="w") as zipf: + with zipfile2.ZipFile(fp, mode="w") as zipf: zipf.writestr("foo.txt", b"O, for a Muse of Fire!") zipfiledata = fp.getvalue() @@ -1690,12 +1692,12 @@ def test_damaged_zipfile(self): # a BadZipFile exception is raised when we try to open it for N in range(len(zipfiledata)): fp = io.BytesIO(zipfiledata[:N]) - self.assertRaises(zipfile.BadZipFile, zipfile.ZipFile, fp) + self.assertRaises(zipfile.BadZipFile, zipfile2.ZipFile, fp) def test_is_zip_valid_file(self): """Check that is_zipfile() correctly identifies zip files.""" # - passing a filename - with zipfile.ZipFile(TESTFN, mode="w") as zipf: + with zipfile2.ZipFile(TESTFN, mode="w") as zipf: zipf.writestr("foo.txt", b"O, for a Muse of Fire!") self.assertTrue(zipfile.is_zipfile(TESTFN)) @@ -1723,21 +1725,21 @@ def test_non_existent_file_raises_OSError(self): # it is ignored, but the user should be sufficiently annoyed by # the message on the output that regression will be noticed # quickly. - self.assertRaises(OSError, zipfile.ZipFile, TESTFN) + self.assertRaises(OSError, zipfile2.ZipFile, TESTFN) def test_empty_file_raises_BadZipFile(self): f = open(TESTFN, 'w') f.close() - self.assertRaises(zipfile.BadZipFile, zipfile.ZipFile, TESTFN) + self.assertRaises(zipfile.BadZipFile, zipfile2.ZipFile, TESTFN) with open(TESTFN, 'w') as fp: fp.write("short file") - self.assertRaises(zipfile.BadZipFile, zipfile.ZipFile, TESTFN) + self.assertRaises(zipfile.BadZipFile, zipfile2.ZipFile, TESTFN) def test_closed_zip_raises_ValueError(self): """Verify that testzip() doesn't swallow inappropriate exceptions.""" data = io.BytesIO() - with zipfile.ZipFile(data, mode="w") as zipf: + with zipfile2.ZipFile(data, mode="w") as zipf: zipf.writestr("foo.txt", "O, for a Muse of Fire!") # This is correct; calling .read on a closed ZipFile should raise @@ -1754,14 +1756,14 @@ def test_closed_zip_raises_ValueError(self): def test_bad_constructor_mode(self): """Check that bad modes passed to ZipFile constructor are caught.""" - self.assertRaises(ValueError, zipfile.ZipFile, TESTFN, "q") + self.assertRaises(ValueError, zipfile2.ZipFile, TESTFN, "q") def test_bad_open_mode(self): """Check that bad modes passed to ZipFile.open are caught.""" - with zipfile.ZipFile(TESTFN, mode="w") as zipf: + with zipfile2.ZipFile(TESTFN, mode="w") as zipf: zipf.writestr("foo.txt", "O, for a Muse of Fire!") - with zipfile.ZipFile(TESTFN, mode="r") as zipf: + with zipfile2.ZipFile(TESTFN, mode="r") as zipf: # read the data to make sure the file is there zipf.read("foo.txt") self.assertRaises(ValueError, zipf.open, "foo.txt", "q") @@ -1772,7 +1774,7 @@ def test_bad_open_mode(self): def test_read0(self): """Check that calling read(0) on a ZipExtFile object returns an empty string and doesn't advance file pointer.""" - with zipfile.ZipFile(TESTFN, mode="w") as zipf: + with zipfile2.ZipFile(TESTFN, mode="w") as zipf: zipf.writestr("foo.txt", "O, for a Muse of Fire!") # read the data to make sure the file is there with zipf.open("foo.txt") as f: @@ -1784,13 +1786,13 @@ def test_read0(self): def test_open_non_existent_item(self): """Check that attempting to call open() for an item that doesn't exist in the archive raises a RuntimeError.""" - with zipfile.ZipFile(TESTFN, mode="w") as zipf: + with zipfile2.ZipFile(TESTFN, mode="w") as zipf: self.assertRaises(KeyError, zipf.open, "foo.txt", "r") def test_bad_compression_mode(self): """Check that bad compression methods passed to ZipFile.open are caught.""" - self.assertRaises(NotImplementedError, zipfile.ZipFile, TESTFN, "w", -1) + self.assertRaises(NotImplementedError, zipfile2.ZipFile, TESTFN, "w", -1) def test_unsupported_compression(self): # data is declared as shrunk, but actually deflated @@ -1800,13 +1802,13 @@ def test_unsupported_compression(self): b'\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' b'\x80\x01\x00\x00\x00\x00xPK\x05\x06\x00\x00\x00\x00\x01\x00\x01\x00' b'/\x00\x00\x00!\x00\x00\x00\x00\x00') - with zipfile.ZipFile(io.BytesIO(data), 'r') as zipf: + with zipfile2.ZipFile(io.BytesIO(data), 'r') as zipf: self.assertRaises(NotImplementedError, zipf.open, 'x') def test_null_byte_in_filename(self): """Check that a filename containing a null byte is properly terminated.""" - with zipfile.ZipFile(TESTFN, mode="w") as zipf: + with zipfile2.ZipFile(TESTFN, mode="w") as zipf: zipf.writestr("foo.txt\x00qqq", b"O, for a Muse of Fire!") self.assertEqual(zipf.namelist(), ['foo.txt']) @@ -1821,96 +1823,96 @@ def test_comments(self): """Check that comments on the archive are handled properly.""" # check default comment is empty - with zipfile.ZipFile(TESTFN, mode="w") as zipf: + with zipfile2.ZipFile(TESTFN, mode="w") as zipf: self.assertEqual(zipf.comment, b'') zipf.writestr("foo.txt", "O, for a Muse of Fire!") - with zipfile.ZipFile(TESTFN, mode="r") as zipfr: + with zipfile2.ZipFile(TESTFN, mode="r") as zipfr: self.assertEqual(zipfr.comment, b'') # check a simple short comment comment = b'Bravely taking to his feet, he beat a very brave retreat.' - with zipfile.ZipFile(TESTFN, mode="w") as zipf: + with zipfile2.ZipFile(TESTFN, mode="w") as zipf: zipf.comment = comment zipf.writestr("foo.txt", "O, for a Muse of Fire!") - with zipfile.ZipFile(TESTFN, mode="r") as zipfr: + with zipfile2.ZipFile(TESTFN, mode="r") as zipfr: self.assertEqual(zipf.comment, comment) # check a comment of max length comment2 = ''.join(['%d' % (i**3 % 10) for i in range((1 << 16)-1)]) comment2 = comment2.encode("ascii") - with zipfile.ZipFile(TESTFN, mode="w") as zipf: + with zipfile2.ZipFile(TESTFN, mode="w") as zipf: zipf.comment = comment2 zipf.writestr("foo.txt", "O, for a Muse of Fire!") - with zipfile.ZipFile(TESTFN, mode="r") as zipfr: + with zipfile2.ZipFile(TESTFN, mode="r") as zipfr: self.assertEqual(zipfr.comment, comment2) # check a comment that is too long is truncated - with zipfile.ZipFile(TESTFN, mode="w") as zipf: + with zipfile2.ZipFile(TESTFN, mode="w") as zipf: with self.assertWarns(UserWarning): zipf.comment = comment2 + b'oops' zipf.writestr("foo.txt", "O, for a Muse of Fire!") - with zipfile.ZipFile(TESTFN, mode="r") as zipfr: + with zipfile2.ZipFile(TESTFN, mode="r") as zipfr: self.assertEqual(zipfr.comment, comment2) # check that comments are correctly modified in append mode - with zipfile.ZipFile(TESTFN,mode="w") as zipf: + with zipfile2.ZipFile(TESTFN,mode="w") as zipf: zipf.comment = b"original comment" zipf.writestr("foo.txt", "O, for a Muse of Fire!") - with zipfile.ZipFile(TESTFN,mode="a") as zipf: + with zipfile2.ZipFile(TESTFN,mode="a") as zipf: zipf.comment = b"an updated comment" - with zipfile.ZipFile(TESTFN,mode="r") as zipf: + with zipfile2.ZipFile(TESTFN,mode="r") as zipf: self.assertEqual(zipf.comment, b"an updated comment") # check that comments are correctly shortened in append mode # and the file is indeed truncated - with zipfile.ZipFile(TESTFN,mode="w") as zipf: + with zipfile2.ZipFile(TESTFN,mode="w") as zipf: zipf.comment = b"original comment that's longer" zipf.writestr("foo.txt", "O, for a Muse of Fire!") original_zip_size = os.path.getsize(TESTFN) - with zipfile.ZipFile(TESTFN,mode="a") as zipf: + with zipfile2.ZipFile(TESTFN,mode="a") as zipf: zipf.comment = b"shorter comment" self.assertTrue(original_zip_size > os.path.getsize(TESTFN)) - with zipfile.ZipFile(TESTFN,mode="r") as zipf: + with zipfile2.ZipFile(TESTFN,mode="r") as zipf: self.assertEqual(zipf.comment, b"shorter comment") def test_unicode_comment(self): - with zipfile.ZipFile(TESTFN, "w", zipfile.ZIP_STORED) as zipf: + with zipfile2.ZipFile(TESTFN, "w", zipfile.ZIP_STORED) as zipf: zipf.writestr("foo.txt", "O, for a Muse of Fire!") with self.assertRaises(TypeError): zipf.comment = "this is an error" def test_change_comment_in_empty_archive(self): - with zipfile.ZipFile(TESTFN, "a", zipfile.ZIP_STORED) as zipf: + with zipfile2.ZipFile(TESTFN, "a", zipfile.ZIP_STORED) as zipf: self.assertFalse(zipf.filelist) zipf.comment = b"this is a comment" - with zipfile.ZipFile(TESTFN, "r") as zipf: + with zipfile2.ZipFile(TESTFN, "r") as zipf: self.assertEqual(zipf.comment, b"this is a comment") def test_change_comment_in_nonempty_archive(self): - with zipfile.ZipFile(TESTFN, "w", zipfile.ZIP_STORED) as zipf: + with zipfile2.ZipFile(TESTFN, "w", zipfile.ZIP_STORED) as zipf: zipf.writestr("foo.txt", "O, for a Muse of Fire!") - with zipfile.ZipFile(TESTFN, "a", zipfile.ZIP_STORED) as zipf: + with zipfile2.ZipFile(TESTFN, "a", zipfile.ZIP_STORED) as zipf: self.assertTrue(zipf.filelist) zipf.comment = b"this is a comment" - with zipfile.ZipFile(TESTFN, "r") as zipf: + with zipfile2.ZipFile(TESTFN, "r") as zipf: self.assertEqual(zipf.comment, b"this is a comment") def test_empty_zipfile(self): # Check that creating a file in 'w' or 'a' mode and closing without # adding any files to the archives creates a valid empty ZIP file - zipf = zipfile.ZipFile(TESTFN, mode="w") + zipf = zipfile2.ZipFile(TESTFN, mode="w") zipf.close() try: - zipf = zipfile.ZipFile(TESTFN, mode="r") + zipf = zipfile2.ZipFile(TESTFN, mode="r") except zipfile.BadZipFile: self.fail("Unable to create empty ZIP file in 'w' mode") - zipf = zipfile.ZipFile(TESTFN, mode="a") + zipf = zipfile2.ZipFile(TESTFN, mode="a") zipf.close() try: - zipf = zipfile.ZipFile(TESTFN, mode="r") + zipf = zipfile2.ZipFile(TESTFN, mode="r") except: self.fail("Unable to create empty ZIP file in 'a' mode") @@ -1920,7 +1922,7 @@ def test_open_empty_file(self): # OSError) f = open(TESTFN, 'w') f.close() - self.assertRaises(zipfile.BadZipFile, zipfile.ZipFile, TESTFN, 'r') + self.assertRaises(zipfile.BadZipFile, zipfile2.ZipFile, TESTFN, 'r') def test_create_zipinfo_before_1980(self): self.assertRaises(ValueError, @@ -1964,7 +1966,7 @@ def test_zipfile_with_short_extra_field(self): b'\x00\x00\x00abc\x00\x00PK\x05\x06\x00\x00\x00\x00' b'\x01\x00\x01\x003\x00\x00\x00%\x00\x00\x00\x00\x00' ) - with zipfile.ZipFile(io.BytesIO(zipdata), 'r') as zipf: + with zipfile2.ZipFile(io.BytesIO(zipdata), 'r') as zipf: # testzip returns the name of the first corrupt file, or None self.assertIsNone(zipf.testzip()) @@ -1973,7 +1975,7 @@ def test_open_conflicting_handles(self): msg1 = b"It's fun to charter an accountant!" msg2 = b"And sail the wide accountant sea" msg3 = b"To find, explore the funds offshore" - with zipfile.ZipFile(TESTFN2, 'w', zipfile.ZIP_STORED) as zipf: + with zipfile2.ZipFile(TESTFN2, 'w', zipfile.ZIP_STORED) as zipf: with zipf.open('foo', mode='w') as w2: w2.write(msg1) with zipf.open('bar', mode='w') as w1: @@ -1991,7 +1993,7 @@ def test_open_conflicting_handles(self): with zipf.open('baz', mode='w') as w2: w2.write(msg3) - with zipfile.ZipFile(TESTFN2, 'r') as zipf: + with zipfile2.ZipFile(TESTFN2, 'r') as zipf: self.assertEqual(zipf.read('foo'), msg1) self.assertEqual(zipf.read('bar'), msg2) self.assertEqual(zipf.read('baz'), msg3) @@ -2002,9 +2004,9 @@ def test_seek_tell(self): txt = b"Where's Bruce?" bloc = txt.find(b"Bruce") # Check seek on a file - with zipfile.ZipFile(TESTFN, "w") as zipf: + with zipfile2.ZipFile(TESTFN, "w") as zipf: zipf.writestr("foo.txt", txt) - with zipfile.ZipFile(TESTFN, "r") as zipf: + with zipfile2.ZipFile(TESTFN, "r") as zipf: with zipf.open("foo.txt", "r") as fp: fp.seek(bloc, os.SEEK_SET) self.assertEqual(fp.tell(), bloc) @@ -2019,9 +2021,9 @@ def test_seek_tell(self): self.assertEqual(fp.tell(), 0) # Check seek on memory file data = io.BytesIO() - with zipfile.ZipFile(data, mode="w") as zipf: + with zipfile2.ZipFile(data, mode="w") as zipf: zipf.writestr("foo.txt", txt) - with zipfile.ZipFile(data, mode="r") as zipf: + with zipfile2.ZipFile(data, mode="r") as zipf: with zipf.open("foo.txt", "r") as fp: fp.seek(bloc, os.SEEK_SET) self.assertEqual(fp.tell(), bloc) @@ -2039,10 +2041,10 @@ def test_seek_tell(self): def test_decompress_without_3rd_party_library(self): data = b'PK\x05\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' zip_file = io.BytesIO(data) - with zipfile.ZipFile(zip_file, 'w', compression=zipfile.ZIP_BZIP2) as zf: + with zipfile2.ZipFile(zip_file, 'w', compression=zipfile.ZIP_BZIP2) as zf: zf.writestr('a.txt', b'a') with mock.patch('zipfile.bz2', None): - with zipfile.ZipFile(zip_file) as zf: + with zipfile2.ZipFile(zip_file) as zf: self.assertRaises(RuntimeError, zf.extract, 'a.txt') @requires_zlib() @@ -2060,7 +2062,7 @@ def test_full_overlap(self): b'\x06\x00\x00\x00\x00\x02\x00\x02\x00^\x00\x00\x00/\x00\x00' b'\x00\x00\x00' ) - with zipfile.ZipFile(io.BytesIO(data), 'r') as zipf: + with zipfile2.ZipFile(io.BytesIO(data), 'r') as zipf: self.assertEqual(zipf.namelist(), ['a', 'b']) zi = zipf.getinfo('a') self.assertEqual(zi.header_offset, 0) @@ -2091,7 +2093,7 @@ def test_quoted_overlap(self): b'bPK\x05\x06\x00\x00\x00\x00\x02\x00\x02\x00^\x00\x00' b'\x00S\x00\x00\x00\x00\x00' ) - with zipfile.ZipFile(io.BytesIO(data), 'r') as zipf: + with zipfile2.ZipFile(io.BytesIO(data), 'r') as zipf: self.assertEqual(zipf.namelist(), ['a', 'b']) zi = zipf.getinfo('a') self.assertEqual(zi.header_offset, 0) @@ -2115,7 +2117,7 @@ def test_testzip_with_bad_crc(self): """Tests that files with bad CRCs return their name from testzip.""" zipdata = self.zip_with_bad_crc - with zipfile.ZipFile(io.BytesIO(zipdata), mode="r") as zipf: + with zipfile2.ZipFile(io.BytesIO(zipdata), mode="r") as zipf: # testzip returns the name of the first corrupt file, or None self.assertEqual('afile', zipf.testzip()) @@ -2124,16 +2126,16 @@ def test_read_with_bad_crc(self): zipdata = self.zip_with_bad_crc # Using ZipFile.read() - with zipfile.ZipFile(io.BytesIO(zipdata), mode="r") as zipf: + with zipfile2.ZipFile(io.BytesIO(zipdata), mode="r") as zipf: self.assertRaises(zipfile.BadZipFile, zipf.read, 'afile') # Using ZipExtFile.read() - with zipfile.ZipFile(io.BytesIO(zipdata), mode="r") as zipf: + with zipfile2.ZipFile(io.BytesIO(zipdata), mode="r") as zipf: with zipf.open('afile', 'r') as corrupt_file: self.assertRaises(zipfile.BadZipFile, corrupt_file.read) # Same with small reads (in order to exercise the buffering logic) - with zipfile.ZipFile(io.BytesIO(zipdata), mode="r") as zipf: + with zipfile2.ZipFile(io.BytesIO(zipdata), mode="r") as zipf: with zipf.open('afile', 'r') as corrupt_file: corrupt_file.MIN_READ_SIZE = 2 with self.assertRaises(zipfile.BadZipFile): @@ -2226,10 +2228,10 @@ class DecryptionTests(unittest.TestCase): def setUp(self): with open(TESTFN, "wb") as fp: fp.write(self.data) - self.zip = zipfile.ZipFile(TESTFN, "r") + self.zip = zipfile2.ZipFile(TESTFN, "r") with open(TESTFN2, "wb") as fp: fp.write(self.data2) - self.zip2 = zipfile.ZipFile(TESTFN2, "r") + self.zip2 = zipfile2.ZipFile(TESTFN2, "r") def tearDown(self): self.zip.close() @@ -2318,7 +2320,7 @@ def tearDown(self): def make_test_archive(self, f, compression): # Create the ZIP archive - with zipfile.ZipFile(f, "w", compression) as zipfp: + with zipfile2.ZipFile(f, "w", compression) as zipfp: zipfp.write(TESTFN, "another.name") zipfp.write(TESTFN, TESTFN) @@ -2326,7 +2328,7 @@ def zip_test(self, f, compression): self.make_test_archive(f, compression) # Read the ZIP archive - with zipfile.ZipFile(f, "r", compression) as zipfp: + with zipfile2.ZipFile(f, "r", compression) as zipfp: testdata = zipfp.read(TESTFN) self.assertEqual(len(testdata), len(self.data)) self.assertEqual(testdata, self.data) @@ -2340,7 +2342,7 @@ def zip_open_test(self, f, compression): self.make_test_archive(f, compression) # Read the ZIP archive - with zipfile.ZipFile(f, "r", compression) as zipfp: + with zipfile2.ZipFile(f, "r", compression) as zipfp: zipdata1 = [] with zipfp.open(TESTFN) as zipopen1: while True: @@ -2373,7 +2375,7 @@ def zip_random_open_test(self, f, compression): self.make_test_archive(f, compression) # Read the ZIP archive - with zipfile.ZipFile(f, "r", compression) as zipfp: + with zipfile2.ZipFile(f, "r", compression) as zipfp: zipdata1 = [] with zipfp.open(TESTFN) as zipopen1: while True: @@ -2445,11 +2447,11 @@ def test_writestr(self): f = io.BytesIO() f.write(b'abc') bf = io.BufferedWriter(f) - with zipfile.ZipFile(wrapper(bf), 'w', zipfile.ZIP_STORED) as zipfp: + with zipfile2.ZipFile(wrapper(bf), 'w', zipfile.ZIP_STORED) as zipfp: zipfp.writestr('ones', b'111') zipfp.writestr('twos', b'222') self.assertEqual(f.getvalue()[:5], b'abcPK') - with zipfile.ZipFile(f, mode='r') as zipf: + with zipfile2.ZipFile(f, mode='r') as zipf: with zipf.open('ones') as zopen: self.assertEqual(zopen.read(), b'111') with zipf.open('twos') as zopen: @@ -2461,7 +2463,7 @@ def test_write(self): f = io.BytesIO() f.write(b'abc') bf = io.BufferedWriter(f) - with zipfile.ZipFile(wrapper(bf), 'w', zipfile.ZIP_STORED) as zipfp: + with zipfile2.ZipFile(wrapper(bf), 'w', zipfile.ZIP_STORED) as zipfp: self.addCleanup(unlink, TESTFN) with open(TESTFN, 'wb') as f2: f2.write(b'111') @@ -2470,7 +2472,7 @@ def test_write(self): f2.write(b'222') zipfp.write(TESTFN, 'twos') self.assertEqual(f.getvalue()[:5], b'abcPK') - with zipfile.ZipFile(f, mode='r') as zipf: + with zipfile2.ZipFile(f, mode='r') as zipf: with zipf.open('ones') as zopen: self.assertEqual(zopen.read(), b'111') with zipf.open('twos') as zopen: @@ -2482,13 +2484,13 @@ def test_open_write(self): f = io.BytesIO() f.write(b'abc') bf = io.BufferedWriter(f) - with zipfile.ZipFile(wrapper(bf), 'w', zipfile.ZIP_STORED) as zipf: + with zipfile2.ZipFile(wrapper(bf), 'w', zipfile.ZIP_STORED) as zipf: with zipf.open('ones', 'w') as zopen: zopen.write(b'111') with zipf.open('twos', 'w') as zopen: zopen.write(b'222') self.assertEqual(f.getvalue()[:5], b'abcPK') - with zipfile.ZipFile(f) as zipf: + with zipfile2.ZipFile(f) as zipf: self.assertEqual(zipf.read('ones'), b'111') self.assertEqual(zipf.read('twos'), b'222') @@ -2502,7 +2504,7 @@ def setUpClass(cls): def make_test_archive(self, f): # Create the ZIP archive - with zipfile.ZipFile(f, "w", zipfile.ZIP_DEFLATED) as zipfp: + with zipfile2.ZipFile(f, "w", zipfile.ZIP_DEFLATED) as zipfp: zipfp.writestr('ones', self.data1) zipfp.writestr('twos', self.data2) @@ -2511,7 +2513,7 @@ def test_same_file(self): # multiple open() calls can be made without interfering with each other. for f in get_files(self): self.make_test_archive(f) - with zipfile.ZipFile(f, mode="r") as zipf: + with zipfile2.ZipFile(f, mode="r") as zipf: with zipf.open('ones') as zopen1, zipf.open('ones') as zopen2: data1 = zopen1.read(500) data2 = zopen2.read(500) @@ -2525,7 +2527,7 @@ def test_different_file(self): # multiple open() calls can be made without interfering with each other. for f in get_files(self): self.make_test_archive(f) - with zipfile.ZipFile(f, mode="r") as zipf: + with zipfile2.ZipFile(f, mode="r") as zipf: with zipf.open('ones') as zopen1, zipf.open('twos') as zopen2: data1 = zopen1.read(500) data2 = zopen2.read(500) @@ -2539,7 +2541,7 @@ def test_interleaved(self): # multiple open() calls can be made without interfering with each other. for f in get_files(self): self.make_test_archive(f) - with zipfile.ZipFile(f, mode="r") as zipf: + with zipfile2.ZipFile(f, mode="r") as zipf: with zipf.open('ones') as zopen1: data1 = zopen1.read(500) with zipf.open('twos') as zopen2: @@ -2553,7 +2555,7 @@ def test_read_after_close(self): for f in get_files(self): self.make_test_archive(f) with contextlib.ExitStack() as stack: - with zipfile.ZipFile(f, 'r') as zipf: + with zipfile2.ZipFile(f, 'r') as zipf: zopen1 = stack.enter_context(zipf.open('ones')) zopen2 = stack.enter_context(zipf.open('twos')) data1 = zopen1.read(500) @@ -2565,13 +2567,13 @@ def test_read_after_close(self): def test_read_after_write(self): for f in get_files(self): - with zipfile.ZipFile(f, 'w', zipfile.ZIP_DEFLATED) as zipf: + with zipfile2.ZipFile(f, 'w', zipfile.ZIP_DEFLATED) as zipf: zipf.writestr('ones', self.data1) zipf.writestr('twos', self.data2) with zipf.open('ones') as zopen1: data1 = zopen1.read(500) self.assertEqual(data1, self.data1[:500]) - with zipfile.ZipFile(f, 'r') as zipf: + with zipfile2.ZipFile(f, 'r') as zipf: data1 = zipf.read('ones') data2 = zipf.read('twos') self.assertEqual(data1, self.data1) @@ -2579,12 +2581,12 @@ def test_read_after_write(self): def test_write_after_read(self): for f in get_files(self): - with zipfile.ZipFile(f, "w", zipfile.ZIP_DEFLATED) as zipf: + with zipfile2.ZipFile(f, "w", zipfile.ZIP_DEFLATED) as zipf: zipf.writestr('ones', self.data1) with zipf.open('ones') as zopen1: zopen1.read(500) zipf.writestr('twos', self.data2) - with zipfile.ZipFile(f, 'r') as zipf: + with zipfile2.ZipFile(f, 'r') as zipf: data1 = zipf.read('ones') data2 = zipf.read('twos') self.assertEqual(data1, self.data1) @@ -2594,7 +2596,7 @@ def test_many_opens(self): # Verify that read() and open() promptly close the file descriptor, # and don't rely on the garbage collector to free resources. self.make_test_archive(TESTFN2) - with zipfile.ZipFile(TESTFN2, mode="r") as zipf: + with zipfile2.ZipFile(TESTFN2, mode="r") as zipf: for x in range(100): zipf.read('ones') with zipf.open('ones') as zopen1: @@ -2603,16 +2605,16 @@ def test_many_opens(self): self.assertLess(f.fileno(), 100) def test_write_while_reading(self): - with zipfile.ZipFile(TESTFN2, 'w', zipfile.ZIP_DEFLATED) as zipf: + with zipfile2.ZipFile(TESTFN2, 'w', zipfile.ZIP_DEFLATED) as zipf: zipf.writestr('ones', self.data1) - with zipfile.ZipFile(TESTFN2, 'a', zipfile.ZIP_DEFLATED) as zipf: + with zipfile2.ZipFile(TESTFN2, 'a', zipfile.ZIP_DEFLATED) as zipf: with zipf.open('ones', 'r') as r1: data1 = r1.read(500) with zipf.open('twos', 'w') as w1: w1.write(self.data2) data1 += r1.read() self.assertEqual(data1, self.data1) - with zipfile.ZipFile(TESTFN2) as zipf: + with zipfile2.ZipFile(TESTFN2) as zipf: self.assertEqual(zipf.read('twos'), self.data2) def tearDown(self): @@ -2624,7 +2626,7 @@ def setUp(self): os.mkdir(TESTFN2) def test_extract_dir(self): - with zipfile.ZipFile(findfile("zipdir.zip")) as zipf: + with zipfile2.ZipFile(findfile("zipdir.zip")) as zipf: zipf.extractall(TESTFN2) self.assertTrue(os.path.isdir(os.path.join(TESTFN2, "a"))) self.assertTrue(os.path.isdir(os.path.join(TESTFN2, "a", "b"))) @@ -2639,7 +2641,7 @@ def test_write_dir(self): dirpath = os.path.join(TESTFN2, "x") os.mkdir(dirpath) mode = os.stat(dirpath).st_mode & 0xFFFF - with zipfile.ZipFile(TESTFN, "w") as zipf: + with zipfile2.ZipFile(TESTFN, "w") as zipf: zipf.write(dirpath) zinfo = zipf.filelist[0] self.assertTrue(zinfo.filename.endswith("/x/")) @@ -2648,7 +2650,7 @@ def test_write_dir(self): zinfo = zipf.filelist[1] self.assertTrue(zinfo.filename, "y/") self.assertEqual(zinfo.external_attr, (mode << 16) | 0x10) - with zipfile.ZipFile(TESTFN, "r") as zipf: + with zipfile2.ZipFile(TESTFN, "r") as zipf: zinfo = zipf.filelist[0] self.assertTrue(zinfo.filename.endswith("/x/")) self.assertEqual(zinfo.external_attr, (mode << 16) | 0x10) @@ -2663,12 +2665,12 @@ def test_write_dir(self): def test_writestr_dir(self): os.mkdir(os.path.join(TESTFN2, "x")) - with zipfile.ZipFile(TESTFN, "w") as zipf: + with zipfile2.ZipFile(TESTFN, "w") as zipf: zipf.writestr("x/", b'') zinfo = zipf.filelist[0] self.assertEqual(zinfo.filename, "x/") self.assertEqual(zinfo.external_attr, (0o40775 << 16) | 0x10) - with zipfile.ZipFile(TESTFN, "r") as zipf: + with zipfile2.ZipFile(TESTFN, "r") as zipf: zinfo = zipf.filelist[0] self.assertTrue(zinfo.filename.endswith("x/")) self.assertEqual(zinfo.external_attr, (0o40775 << 16) | 0x10) @@ -2751,7 +2753,7 @@ def test_test_command(self): def test_list_command(self): zip_name = findfile('zipdir.zip') t = io.StringIO() - with zipfile.ZipFile(zip_name, 'r') as tf: + with zipfile2.ZipFile(zip_name, 'r') as tf: tf.printdir(t) expected = t.getvalue().encode('ascii', 'backslashreplace') for opt in '-l', '--list': @@ -2774,7 +2776,7 @@ def test_create_command(self): try: out = self.zipfilecmd(opt, TESTFN2, *files) self.assertEqual(out, b'') - with zipfile.ZipFile(TESTFN2) as zf: + with zipfile2.ZipFile(TESTFN2) as zf: self.assertEqual(zf.namelist(), namelist) self.assertEqual(zf.read(namelist[0]), b'test 1') self.assertEqual(zf.read(namelist[2]), b'test 2') @@ -2787,7 +2789,7 @@ def test_extract_command(self): with temp_dir() as extdir: out = self.zipfilecmd(opt, zip_name, extdir) self.assertEqual(out, b'') - with zipfile.ZipFile(zip_name) as zf: + with zipfile2.ZipFile(zip_name) as zf: for zi in zf.infolist(): path = os.path.join(extdir, zi.filename.replace('/', os.sep)) @@ -2811,7 +2813,7 @@ def _test_zip_works(self, name): self.assertTrue(zipfile.is_zipfile(name), f'is_zipfile failed on {name}') # Ensure we can operate on these via ZipFile. - with zipfile.ZipFile(name) as zipfp: + with zipfile2.ZipFile(name) as zipfp: for n in zipfp.namelist(): data = zipfp.read(n) self.assertIn(b'FAVORITE_NUMBER', data) @@ -2894,7 +2896,7 @@ def build_alpharep_fixture(): "rep" because it's a representative example """ data = io.BytesIO() - zf = zipfile.ZipFile(data, "w") + zf = zipfile2.ZipFile(data, "w") zf.writestr("a.txt", b"content of a") zf.writestr("b/c.txt", b"content of c") zf.writestr("b/d/e.txt", b"content of e") @@ -3032,7 +3034,7 @@ def test_mutability(self): def huge_zipfile(self): """Create a read-only zipfile with a huge number of entries entries.""" strm = io.BytesIO() - zf = zipfile.ZipFile(strm, "w") + zf = zipfile2.ZipFile(strm, "w") for entry in map(str, range(self.HUGE_ZIPFILE_NUM_ENTRIES)): zf.writestr(entry, entry) zf.mode = 'r' @@ -3063,7 +3065,7 @@ def test_malformed_paths(self): Paths with dots are treated like regular files. """ data = io.BytesIO() - zf = zipfile.ZipFile(data, "w") + zf = zipfile2.ZipFile(data, "w") zf.writestr("/one-slash.txt", b"content") zf.writestr("//two-slash.txt", b"content") zf.writestr("../parent.txt", b"content") @@ -3081,7 +3083,7 @@ def test_unsupported_names(self): in the zip file. """ data = io.BytesIO() - zf = zipfile.ZipFile(data, "w") + zf = zipfile2.ZipFile(data, "w") zf.writestr("path?", b"content") zf.writestr("V: NMS.flac", b"fLaC...") zf.filename = '' @@ -3096,7 +3098,7 @@ def test_backslash_not_separator(self): In a zip file, backslashes are not separators. """ data = io.BytesIO() - zf = zipfile.ZipFile(data, "w") + zf = zipfile2.ZipFile(data, "w") zf.writestr(DirtyZipInfo.for_name("foo\\bar", zf), b"content") zf.filename = '' root = zipfile.Path(zf) diff --git a/cpython-tests/3.9/test_zipfile64.py b/cpython-tests/3.9/test_zipfile64.py index 3a788de..31d0c92 100644 --- a/cpython-tests/3.9/test_zipfile64.py +++ b/cpython-tests/3.9/test_zipfile64.py @@ -19,6 +19,9 @@ from test.support import TESTFN, requires_zlib +import zipfile2 + + TESTFN2 = TESTFN + "2" # How much time in seconds can pass before we print a 'Still working' message. @@ -36,7 +39,7 @@ def setUp(self): def zipTest(self, f, compression): # Create the ZIP archive. - with zipfile.ZipFile(f, "w", compression) as zipfp: + with zipfile2.ZipFile(f, "w", compression) as zipfp: # It will contain enough copies of self.data to reach about 6 GiB of # raw data to store. @@ -54,7 +57,7 @@ def zipTest(self, f, compression): sys.__stdout__.flush() # Read the ZIP archive - with zipfile.ZipFile(f, "r", compression) as zipfp: + with zipfile2.ZipFile(f, "r", compression) as zipfp: for num in range(filecount): self.assertEqual(zipfp.read("testfn%d" % num), self.data) # Print still working message since this test can be really slow @@ -92,21 +95,21 @@ class OtherTests(unittest.TestCase): def testMoreThan64kFiles(self): # This test checks that more than 64k files can be added to an archive, # and that the resulting archive can be read properly by ZipFile - with zipfile.ZipFile(TESTFN, mode="w", allowZip64=True) as zipf: + with zipfile2.ZipFile(TESTFN, mode="w", allowZip64=True) as zipf: zipf.debug = 100 numfiles = (1 << 16) * 3//2 for i in range(numfiles): zipf.writestr("foo%08d" % i, "%d" % (i**3 % 57)) self.assertEqual(len(zipf.namelist()), numfiles) - with zipfile.ZipFile(TESTFN, mode="r") as zipf2: + with zipfile2.ZipFile(TESTFN, mode="r") as zipf2: self.assertEqual(len(zipf2.namelist()), numfiles) for i in range(numfiles): content = zipf2.read("foo%08d" % i).decode('ascii') self.assertEqual(content, "%d" % (i**3 % 57)) def testMoreThan64kFilesAppend(self): - with zipfile.ZipFile(TESTFN, mode="w", allowZip64=False) as zipf: + with zipfile2.ZipFile(TESTFN, mode="w", allowZip64=False) as zipf: zipf.debug = 100 numfiles = (1 << 16) - 1 for i in range(numfiles): @@ -116,14 +119,14 @@ def testMoreThan64kFilesAppend(self): zipf.writestr("foo%08d" % numfiles, b'') self.assertEqual(len(zipf.namelist()), numfiles) - with zipfile.ZipFile(TESTFN, mode="a", allowZip64=False) as zipf: + with zipfile2.ZipFile(TESTFN, mode="a", allowZip64=False) as zipf: zipf.debug = 100 self.assertEqual(len(zipf.namelist()), numfiles) with self.assertRaises(zipfile.LargeZipFile): zipf.writestr("foo%08d" % numfiles, b'') self.assertEqual(len(zipf.namelist()), numfiles) - with zipfile.ZipFile(TESTFN, mode="a", allowZip64=True) as zipf: + with zipfile2.ZipFile(TESTFN, mode="a", allowZip64=True) as zipf: zipf.debug = 100 self.assertEqual(len(zipf.namelist()), numfiles) numfiles2 = (1 << 16) * 3//2 @@ -131,7 +134,7 @@ def testMoreThan64kFilesAppend(self): zipf.writestr("foo%08d" % i, "%d" % (i**3 % 57)) self.assertEqual(len(zipf.namelist()), numfiles2) - with zipfile.ZipFile(TESTFN, mode="r") as zipf2: + with zipfile2.ZipFile(TESTFN, mode="r") as zipf2: self.assertEqual(len(zipf2.namelist()), numfiles2) for i in range(numfiles2): content = zipf2.read("foo%08d" % i).decode('ascii') diff --git a/cpython-tests/3.9/test_zipimport.py b/cpython-tests/3.9/test_zipimport.py index b7347a3..5b5f2ae 100644 --- a/cpython-tests/3.9/test_zipimport.py +++ b/cpython-tests/3.9/test_zipimport.py @@ -10,7 +10,7 @@ from test import support -from zipfile import ZipFile, ZipInfo, ZIP_STORED, ZIP_DEFLATED +from zipfile import ZipInfo, ZIP_STORED, ZIP_DEFLATED import zipimport import linecache @@ -23,6 +23,9 @@ except ImportError: zlib = None +from zipfile2 import ZipFile + + test_src = """\ def get_name(): return __name__ diff --git a/cpython-tests/3.9/test_zipimport_support.py b/cpython-tests/3.9/test_zipimport_support.py deleted file mode 100644 index 8856101..0000000 --- a/cpython-tests/3.9/test_zipimport_support.py +++ /dev/null @@ -1,242 +0,0 @@ -# This test module covers support in various parts of the standard library -# for working with modules located inside zipfiles -# The tests are centralised in this fashion to make it easy to drop them -# if a platform doesn't support zipimport -import test.support -import os -import os.path -import sys -import textwrap -import zipfile -import zipimport -import doctest -import inspect -import linecache -import unittest -from test.support.script_helper import (spawn_python, kill_python, assert_python_ok, - make_script, make_zip_script) - -verbose = test.support.verbose - -# Library modules covered by this test set -# pdb (Issue 4201) -# inspect (Issue 4223) -# doctest (Issue 4197) - -# Other test modules with zipimport related tests -# test_zipimport (of course!) -# test_cmd_line_script (covers the zipimport support in runpy) - -# Retrieve some helpers from other test cases -from test import (test_doctest, sample_doctest, sample_doctest_no_doctests, - sample_doctest_no_docstrings) - - -def _run_object_doctest(obj, module): - finder = doctest.DocTestFinder(verbose=verbose, recurse=False) - runner = doctest.DocTestRunner(verbose=verbose) - # Use the object's fully qualified name if it has one - # Otherwise, use the module's name - try: - name = "%s.%s" % (obj.__module__, obj.__qualname__) - except AttributeError: - name = module.__name__ - for example in finder.find(obj, name, module): - runner.run(example) - f, t = runner.failures, runner.tries - if f: - raise test.support.TestFailed("%d of %d doctests failed" % (f, t)) - if verbose: - print ('doctest (%s) ... %d tests with zero failures' % (module.__name__, t)) - return f, t - - - -class ZipSupportTests(unittest.TestCase): - # This used to use the ImportHooksBaseTestCase to restore - # the state of the import related information - # in the sys module after each test. However, that restores - # *too much* information and breaks for the invocation - # of test_doctest. So we do our own thing and leave - # sys.modules alone. - # We also clear the linecache and zipimport cache - # just to avoid any bogus errors due to name reuse in the tests - def setUp(self): - linecache.clearcache() - zipimport._zip_directory_cache.clear() - self.path = sys.path[:] - self.meta_path = sys.meta_path[:] - self.path_hooks = sys.path_hooks[:] - sys.path_importer_cache.clear() - - def tearDown(self): - sys.path[:] = self.path - sys.meta_path[:] = self.meta_path - sys.path_hooks[:] = self.path_hooks - sys.path_importer_cache.clear() - - def test_inspect_getsource_issue4223(self): - test_src = "def foo(): pass\n" - with test.support.temp_dir() as d: - init_name = make_script(d, '__init__', test_src) - name_in_zip = os.path.join('zip_pkg', - os.path.basename(init_name)) - zip_name, run_name = make_zip_script(d, 'test_zip', - init_name, name_in_zip) - os.remove(init_name) - sys.path.insert(0, zip_name) - import zip_pkg - try: - self.assertEqual(inspect.getsource(zip_pkg.foo), test_src) - finally: - del sys.modules["zip_pkg"] - - def test_doctest_issue4197(self): - # To avoid having to keep two copies of the doctest module's - # unit tests in sync, this test works by taking the source of - # test_doctest itself, rewriting it a bit to cope with a new - # location, and then throwing it in a zip file to make sure - # everything still works correctly - test_src = inspect.getsource(test_doctest) - test_src = test_src.replace( - "from test import test_doctest", - "import test_zipped_doctest as test_doctest") - test_src = test_src.replace("test.test_doctest", - "test_zipped_doctest") - test_src = test_src.replace("test.sample_doctest", - "sample_zipped_doctest") - # The sample doctest files rewritten to include in the zipped version. - sample_sources = {} - for mod in [sample_doctest, sample_doctest_no_doctests, - sample_doctest_no_docstrings]: - src = inspect.getsource(mod) - src = src.replace("test.test_doctest", "test_zipped_doctest") - # Rewrite the module name so that, for example, - # "test.sample_doctest" becomes "sample_zipped_doctest". - mod_name = mod.__name__.split(".")[-1] - mod_name = mod_name.replace("sample_", "sample_zipped_") - sample_sources[mod_name] = src - - with test.support.temp_dir() as d: - script_name = make_script(d, 'test_zipped_doctest', - test_src) - zip_name, run_name = make_zip_script(d, 'test_zip', - script_name) - with zipfile.ZipFile(zip_name, 'a') as z: - for mod_name, src in sample_sources.items(): - z.writestr(mod_name + ".py", src) - if verbose: - with zipfile.ZipFile(zip_name, 'r') as zip_file: - print ('Contents of %r:' % zip_name) - zip_file.printdir() - os.remove(script_name) - sys.path.insert(0, zip_name) - import test_zipped_doctest - try: - # Some of the doc tests depend on the colocated text files - # which aren't available to the zipped version (the doctest - # module currently requires real filenames for non-embedded - # tests). So we're forced to be selective about which tests - # to run. - # doctest could really use some APIs which take a text - # string or a file object instead of a filename... - known_good_tests = [ - test_zipped_doctest.SampleClass, - test_zipped_doctest.SampleClass.NestedClass, - test_zipped_doctest.SampleClass.NestedClass.__init__, - test_zipped_doctest.SampleClass.__init__, - test_zipped_doctest.SampleClass.a_classmethod, - test_zipped_doctest.SampleClass.a_property, - test_zipped_doctest.SampleClass.a_staticmethod, - test_zipped_doctest.SampleClass.double, - test_zipped_doctest.SampleClass.get, - test_zipped_doctest.SampleNewStyleClass, - test_zipped_doctest.SampleNewStyleClass.__init__, - test_zipped_doctest.SampleNewStyleClass.double, - test_zipped_doctest.SampleNewStyleClass.get, - test_zipped_doctest.sample_func, - test_zipped_doctest.test_DocTest, - test_zipped_doctest.test_DocTestParser, - test_zipped_doctest.test_DocTestRunner.basics, - test_zipped_doctest.test_DocTestRunner.exceptions, - test_zipped_doctest.test_DocTestRunner.option_directives, - test_zipped_doctest.test_DocTestRunner.optionflags, - test_zipped_doctest.test_DocTestRunner.verbose_flag, - test_zipped_doctest.test_Example, - test_zipped_doctest.test_debug, - test_zipped_doctest.test_testsource, - test_zipped_doctest.test_trailing_space_in_test, - test_zipped_doctest.test_DocTestSuite, - test_zipped_doctest.test_DocTestFinder, - ] - # These tests are the ones which need access - # to the data files, so we don't run them - fail_due_to_missing_data_files = [ - test_zipped_doctest.test_DocFileSuite, - test_zipped_doctest.test_testfile, - test_zipped_doctest.test_unittest_reportflags, - ] - - for obj in known_good_tests: - _run_object_doctest(obj, test_zipped_doctest) - finally: - del sys.modules["test_zipped_doctest"] - - def test_doctest_main_issue4197(self): - test_src = textwrap.dedent("""\ - class Test: - ">>> 'line 2'" - pass - - import doctest - doctest.testmod() - """) - pattern = 'File "%s", line 2, in %s' - with test.support.temp_dir() as d: - script_name = make_script(d, 'script', test_src) - rc, out, err = assert_python_ok(script_name) - expected = pattern % (script_name, "__main__.Test") - if verbose: - print ("Expected line", expected) - print ("Got stdout:") - print (ascii(out)) - self.assertIn(expected.encode('utf-8'), out) - zip_name, run_name = make_zip_script(d, "test_zip", - script_name, '__main__.py') - rc, out, err = assert_python_ok(zip_name) - expected = pattern % (run_name, "__main__.Test") - if verbose: - print ("Expected line", expected) - print ("Got stdout:") - print (ascii(out)) - self.assertIn(expected.encode('utf-8'), out) - - def test_pdb_issue4201(self): - test_src = textwrap.dedent("""\ - def f(): - pass - - import pdb - pdb.Pdb(nosigint=True).runcall(f) - """) - with test.support.temp_dir() as d: - script_name = make_script(d, 'script', test_src) - p = spawn_python(script_name) - p.stdin.write(b'l\n') - data = kill_python(p) - # bdb/pdb applies normcase to its filename before displaying - self.assertIn(os.path.normcase(script_name.encode('utf-8')), data) - zip_name, run_name = make_zip_script(d, "test_zip", - script_name, '__main__.py') - p = spawn_python(zip_name) - p.stdin.write(b'l\n') - data = kill_python(p) - # bdb/pdb applies normcase to its filename before displaying - self.assertIn(os.path.normcase(run_name.encode('utf-8')), data) - - -def tearDownModule(): - test.support.reap_children() - -if __name__ == '__main__': - unittest.main() From 00cd502d166d3c389eeaa202a761224206d8b386 Mon Sep 17 00:00:00 2001 From: Ioannis Tziakos Date: Sat, 5 Oct 2024 12:04:26 +0100 Subject: [PATCH 14/41] Use unittest for running the python tests --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index ccbade3..6e9350d 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -26,7 +26,7 @@ jobs: - name: Test with haas run: python -m haas zipfile2 - name: Integration tests - run: python -m haas cpython-tests/${{ matrix.python-version }} + run: python -m unittest discover cpython-tests/${{ matrix.python-version }} code-lint: runs-on: ubuntu-latest From 67721855bf052071265d206f2258e41ce5587b73 Mon Sep 17 00:00:00 2001 From: Ioannis Tziakos Date: Sat, 26 Oct 2024 10:50:04 +0100 Subject: [PATCH 15/41] Use test code from the right version --- cpython-tests/3.11/test_zipfile.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cpython-tests/3.11/test_zipfile.py b/cpython-tests/3.11/test_zipfile.py index 4399863..2004816 100644 --- a/cpython-tests/3.11/test_zipfile.py +++ b/cpython-tests/3.11/test_zipfile.py @@ -1470,10 +1470,10 @@ def test_extract_hackers_arcnames_windows_only(self): (r'C:\foo\bar', 'foo/bar'), (r'//conky/mountpoint/foo/bar', 'foo/bar'), (r'\\conky\mountpoint\foo\bar', 'foo/bar'), - (r'///conky/mountpoint/foo/bar', 'conky/mountpoint/foo/bar'), - (r'\\\conky\mountpoint\foo\bar', 'conky/mountpoint/foo/bar'), - (r'//conky//mountpoint/foo/bar', 'conky/mountpoint/foo/bar'), - (r'\\conky\\mountpoint\foo\bar', 'conky/mountpoint/foo/bar'), + (r'///conky/mountpoint/foo/bar', 'mountpoint/foo/bar'), + (r'\\\conky\mountpoint\foo\bar', 'mountpoint/foo/bar'), + (r'//conky//mountpoint/foo/bar', 'mountpoint/foo/bar'), + (r'\\conky\\mountpoint\foo\bar', 'mountpoint/foo/bar'), (r'//?/C:/foo/bar', 'foo/bar'), (r'\\?\C:\foo\bar', 'foo/bar'), (r'C:/../C:/foo/bar', 'C_/foo/bar'), From 59732c65e29723cb2362a29029ed15506e106701 Mon Sep 17 00:00:00 2001 From: Ioannis Tziakos Date: Sat, 26 Oct 2024 10:59:07 +0100 Subject: [PATCH 16/41] Fix Python 3.11 compatibility --- zipfile2/_zipfile.py | 28 ++++++++++------------------ 1 file changed, 10 insertions(+), 18 deletions(-) diff --git a/zipfile2/_zipfile.py b/zipfile2/_zipfile.py index ef72e12..3f3bc99 100644 --- a/zipfile2/_zipfile.py +++ b/zipfile2/_zipfile.py @@ -147,14 +147,12 @@ def extractall( def extract_to(self, member, destination, path=None, pwd=None, preserve_permissions=PERMS_PRESERVE_NONE): + if not isinstance(member, zipfile.ZipInfo): member = self.getinfo(member) - if path is None: - path = os.getcwd() - - return self._extract_member_to(member, destination, path, pwd, - preserve_permissions) + return self._extract_member_to( + member, destination, path, pwd, preserve_permissions) def write( self, filename, arcname=None, @@ -223,20 +221,14 @@ def _extract_member_to(self, member, arcname, targetpath, pwd, # interpret absolute pathname as relative, remove drive letter or # UNC path, redundant separators, "." and ".." components. arcname = os.path.splitdrive(arcname)[1] - arcname = os.path.sep.join(x for x in arcname.split(os.path.sep) - if x not in ('', os.path.curdir, - os.path.pardir)) + invalid_path_parts = ('', os.path.curdir, os.path.pardir) + arcname = os.path.sep.join( + x for x in arcname.split(os.path.sep) + if x not in invalid_path_parts) + if os.path.sep == '\\': # filter illegal characters on Windows - illegal = ':<>|"?*' - if isinstance(arcname, str): - table = dict((ord(c), ord('_')) for c in illegal) - else: - table = string.maketrans(illegal, '_' * len(illegal)) - arcname = arcname.translate(table) - # remove trailing dots - arcname = (x.rstrip('.') for x in arcname.split(os.path.sep)) - arcname = os.path.sep.join(x for x in arcname if x) + arcname = self._sanitize_windows_name(arcname, os.path.sep) targetpath = os.path.join(targetpath, arcname) targetpath = os.path.normpath(targetpath) @@ -246,7 +238,7 @@ def _extract_member_to(self, member, arcname, targetpath, pwd, if upperdirs and not os.path.exists(upperdirs): os.makedirs(upperdirs) - if member.filename[-1] == '/': + if member.is_dir(): if not os.path.isdir(targetpath): os.mkdir(targetpath) return targetpath From e5dabd95926e3c2e116436d1c903d4f15a01a772 Mon Sep 17 00:00:00 2001 From: Ioannis Tziakos Date: Sat, 26 Oct 2024 11:27:31 +0100 Subject: [PATCH 17/41] Remvoe tests that test the support functions --- .../test_zipfile/_path/test_complexity.py | 103 ------------------ 1 file changed, 103 deletions(-) delete mode 100644 cpython-tests/3.12/test_zipfile/_path/test_complexity.py diff --git a/cpython-tests/3.12/test_zipfile/_path/test_complexity.py b/cpython-tests/3.12/test_zipfile/_path/test_complexity.py deleted file mode 100644 index 2286176..0000000 --- a/cpython-tests/3.12/test_zipfile/_path/test_complexity.py +++ /dev/null @@ -1,103 +0,0 @@ -import io -import itertools -import math -import re -import string -import unittest -import zipfile - -from ._functools import compose -from ._itertools import consume - -from ._support import import_or_skip - - -big_o = import_or_skip('big_o') -pytest = import_or_skip('pytest') - - -class TestComplexity(unittest.TestCase): - @pytest.mark.flaky - def test_implied_dirs_performance(self): - best, others = big_o.big_o( - compose(consume, zipfile._path.CompleteDirs._implied_dirs), - lambda size: [ - '/'.join(string.ascii_lowercase + str(n)) for n in range(size) - ], - max_n=1000, - min_n=1, - ) - assert best <= big_o.complexities.Linear - - def make_zip_path(self, depth=1, width=1) -> zipfile.Path: - """ - Construct a Path with width files at every level of depth. - """ - zf = zipfile.ZipFile(io.BytesIO(), mode='w') - pairs = itertools.product(self.make_deep_paths(depth), self.make_names(width)) - for path, name in pairs: - zf.writestr(f"{path}{name}.txt", b'') - zf.filename = "big un.zip" - return zipfile.Path(zf) - - @classmethod - def make_names(cls, width, letters=string.ascii_lowercase): - """ - >>> list(TestComplexity.make_names(2)) - ['a', 'b'] - >>> list(TestComplexity.make_names(30)) - ['aa', 'ab', ..., 'bd'] - """ - # determine how many products are needed to produce width - n_products = math.ceil(math.log(width, len(letters))) - inputs = (letters,) * n_products - combinations = itertools.product(*inputs) - names = map(''.join, combinations) - return itertools.islice(names, width) - - @classmethod - def make_deep_paths(cls, depth): - return map(cls.make_deep_path, range(depth)) - - @classmethod - def make_deep_path(cls, depth): - return ''.join(('d/',) * depth) - - def test_baseline_regex_complexity(self): - best, others = big_o.big_o( - lambda path: re.fullmatch(r'[^/]*\\.txt', path), - self.make_deep_path, - max_n=100, - min_n=1, - ) - assert best <= big_o.complexities.Constant - - @pytest.mark.flaky - def test_glob_depth(self): - best, others = big_o.big_o( - lambda path: consume(path.glob('*.txt')), - self.make_zip_path, - max_n=100, - min_n=1, - ) - assert best <= big_o.complexities.Quadratic - - @pytest.mark.flaky - def test_glob_width(self): - best, others = big_o.big_o( - lambda path: consume(path.glob('*.txt')), - lambda size: self.make_zip_path(width=size), - max_n=100, - min_n=1, - ) - assert best <= big_o.complexities.Linear - - @pytest.mark.flaky - def test_glob_width_and_depth(self): - best, others = big_o.big_o( - lambda path: consume(path.glob('*.txt')), - lambda size: self.make_zip_path(depth=size, width=size), - max_n=10, - min_n=1, - ) - assert best <= big_o.complexities.Linear From 0b2e26364c157936c1dedbe19eebead834b2d5a6 Mon Sep 17 00:00:00 2001 From: Ioannis Tziakos Date: Sat, 26 Oct 2024 12:08:00 +0100 Subject: [PATCH 18/41] Fix flake8 errors --- zipfile2/_zipfile.py | 1 - 1 file changed, 1 deletion(-) diff --git a/zipfile2/_zipfile.py b/zipfile2/_zipfile.py index 3f3bc99..fcd00f5 100644 --- a/zipfile2/_zipfile.py +++ b/zipfile2/_zipfile.py @@ -2,7 +2,6 @@ import os import shutil import stat -import string import time import zipfile import warnings From 3f4bf94daccfe396422c15b44453572599908622 Mon Sep 17 00:00:00 2001 From: Ioannis Tziakos Date: Sat, 26 Oct 2024 12:17:27 +0100 Subject: [PATCH 19/41] Update test.yml use haas to run the integration tests --- .github/workflows/test.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 6e9350d..12e7359 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -26,7 +26,8 @@ jobs: - name: Test with haas run: python -m haas zipfile2 - name: Integration tests - run: python -m unittest discover cpython-tests/${{ matrix.python-version }} + run: python -m haas . + working_directory: cpython-tests/${{ matrix.python-version }} code-lint: runs-on: ubuntu-latest From fe867c36b3891545cec8ebfa08464bd3609ecb9f Mon Sep 17 00:00:00 2001 From: Ioannis Tziakos Date: Sat, 26 Oct 2024 12:23:37 +0100 Subject: [PATCH 20/41] fix-typo --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 12e7359..97c6719 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -27,7 +27,7 @@ jobs: run: python -m haas zipfile2 - name: Integration tests run: python -m haas . - working_directory: cpython-tests/${{ matrix.python-version }} + working-directory: cpython-tests/${{ matrix.python-version }} code-lint: runs-on: ubuntu-latest From 8dacc71ee6349a65712ed607ee2d98e02fb34c21 Mon Sep 17 00:00:00 2001 From: Ioannis Tziakos Date: Sat, 26 Oct 2024 12:28:47 +0100 Subject: [PATCH 21/41] Update test.yml try only linux for now --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 97c6719..4b17b8c 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -11,7 +11,7 @@ jobs: strategy: matrix: python-version: ['3.8', '3.9', '3.10', '3.11', '3.12'] - os: [ubuntu-latest, macos-latest, windows-latest] + os: [ubuntu-latest] runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v3 From 12bfc3e072c84ecba1f29014ac8de10b1735258d Mon Sep 17 00:00:00 2001 From: Ioannis Tziakos Date: Sun, 10 Nov 2024 13:45:22 +0000 Subject: [PATCH 22/41] Update Python 3.10 integration tests - Do not check for duplicate entries by default --- cpython-tests/3.10/test_zipfile.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cpython-tests/3.10/test_zipfile.py b/cpython-tests/3.10/test_zipfile.py index 7ac8ffa..2eecbc1 100644 --- a/cpython-tests/3.10/test_zipfile.py +++ b/cpython-tests/3.10/test_zipfile.py @@ -1535,13 +1535,14 @@ def _test_extract_hackers_arcnames(self, hacknames): class OtherTests(unittest.TestCase): def test_open_via_zip_info(self): # Create the ZIP archive - with zipfile2.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED) as zipfp: + with zipfile2.ZipFile( + TESTFN2, "w", zipfile.ZIP_STORED, low_level=True) as zipfp: zipfp.writestr("name", "foo") with self.assertWarns(UserWarning): zipfp.writestr("name", "bar") self.assertEqual(zipfp.namelist(), ["name"] * 2) - with zipfile2.ZipFile(TESTFN2, "r") as zipfp: + with zipfile2.ZipFile(TESTFN2, "r", low_level=True) as zipfp: infos = zipfp.infolist() data = b"" for info in infos: From 45e80275f418c8a9dcb05045e32727573c60c510 Mon Sep 17 00:00:00 2001 From: Ioannis Tziakos Date: Sat, 4 Jan 2025 11:23:14 +0000 Subject: [PATCH 23/41] Fix test_open_via_zip_info for Python 3.9 --- cpython-tests/3.9/test_zipfile.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cpython-tests/3.9/test_zipfile.py b/cpython-tests/3.9/test_zipfile.py index 6531d6d..a4a8e73 100644 --- a/cpython-tests/3.9/test_zipfile.py +++ b/cpython-tests/3.9/test_zipfile.py @@ -1530,15 +1530,16 @@ def _test_extract_hackers_arcnames(self, hacknames): class OtherTests(unittest.TestCase): + def test_open_via_zip_info(self): # Create the ZIP archive - with zipfile2.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED) as zipfp: + with zipfile2.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED, low_level=True) as zipfp: zipfp.writestr("name", "foo") with self.assertWarns(UserWarning): zipfp.writestr("name", "bar") self.assertEqual(zipfp.namelist(), ["name"] * 2) - with zipfile2.ZipFile(TESTFN2, "r") as zipfp: + with zipfile2.ZipFile(TESTFN2, "r", low_level=True) as zipfp: infos = zipfp.infolist() data = b"" for info in infos: From 9ac3ff6106563026c371a8dd9550a2e0fa4f9089 Mon Sep 17 00:00:00 2001 From: Ioannis Tziakos Date: Sat, 4 Jan 2025 12:08:51 +0000 Subject: [PATCH 24/41] Fix import for the test example string --- cpython-tests/3.10/test_zipfile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpython-tests/3.10/test_zipfile.py b/cpython-tests/3.10/test_zipfile.py index 2eecbc1..f0c47cb 100644 --- a/cpython-tests/3.10/test_zipfile.py +++ b/cpython-tests/3.10/test_zipfile.py @@ -3045,7 +3045,7 @@ def test_open_encoding_errors(self): def test_encoding_warnings(self): """EncodingWarning must blame the read_text and open calls.""" code = '''\ -import io, zipfile +import io, zipfile2, zipfile with zipfile2.ZipFile(io.BytesIO(), "w") as zf: zf.filename = '' zf.writestr("path/file.txt", b"Spanish Inquisition") From 113b2cdbb936a0db21b5dcef63a6ebef44f0d2ba Mon Sep 17 00:00:00 2001 From: Ioannis Tziakos Date: Sat, 8 Feb 2025 13:55:57 +0000 Subject: [PATCH 25/41] Skip cpython 3.12 tests that are expected to failu --- cpython-tests/3.12/test_zipfile/test_core.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/cpython-tests/3.12/test_zipfile/test_core.py b/cpython-tests/3.12/test_zipfile/test_core.py index b094c1d..8e52321 100644 --- a/cpython-tests/3.12/test_zipfile/test_core.py +++ b/cpython-tests/3.12/test_zipfile/test_core.py @@ -883,10 +883,9 @@ def test_append(self): self.assertEqual(zinfo.extra, extra) def make_zip64_file( - self, file_size_64_set=False, file_size_extra=False, - compress_size_64_set=False, compress_size_extra=False, - header_offset_64_set=False, header_offset_extra=False, - ): + self, file_size_64_set=False, file_size_extra=False, + compress_size_64_set=False, compress_size_extra=False, + header_offset_64_set=False, header_offset_extra=False,): """Generate bytes sequence for a zip with (incomplete) zip64 data. The actual values (not the zip 64 0xffffffff values) stored in the file @@ -1557,6 +1556,12 @@ def open(self, path): def extractall(self, ar): ar.extractall(self.testdir) + def test_overwrite_file_symlink_as_file(self): + self.skipTest('Zipfile2 will overwrite symlinks with a real file') + + def test_overwrite_broken_file_symlink_as_file(self): + self.skipTest('Zipfile2 will overwrite symlinks with a real file') + class OtherTests(unittest.TestCase): def test_open_via_zip_info(self): From d844842bb3f75ba73483967af7ea259a59fb820b Mon Sep 17 00:00:00 2001 From: Ioannis Tziakos Date: Sat, 8 Feb 2025 13:59:49 +0000 Subject: [PATCH 26/41] Copy check from Python 3.12 --- zipfile2/_zipfile.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/zipfile2/_zipfile.py b/zipfile2/_zipfile.py index fcd00f5..431ef5d 100644 --- a/zipfile2/_zipfile.py +++ b/zipfile2/_zipfile.py @@ -229,6 +229,9 @@ def _extract_member_to(self, member, arcname, targetpath, pwd, # filter illegal characters on Windows arcname = self._sanitize_windows_name(arcname, os.path.sep) + if not arcname and not member.is_dir(): + raise ValueError("Empty filename.") + targetpath = os.path.join(targetpath, arcname) targetpath = os.path.normpath(targetpath) From a24fa3f54ac14f87d31977805326f193fe8f485c Mon Sep 17 00:00:00 2001 From: Ioannis Tziakos Date: Sat, 8 Feb 2025 14:10:09 +0000 Subject: [PATCH 27/41] Test on windows and macos --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 4b17b8c..223c176 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -11,7 +11,7 @@ jobs: strategy: matrix: python-version: ['3.8', '3.9', '3.10', '3.11', '3.12'] - os: [ubuntu-latest] + os: [ubuntu-latest, windows-latest, macos-latest] runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v3 From a73df21e6089e4b5469dd67d8afde36edc3ace14 Mon Sep 17 00:00:00 2001 From: Ioannis Tziakos Date: Sun, 9 Feb 2025 13:37:09 +0000 Subject: [PATCH 28/41] Update cpython 3.12 integration tests - Skip failing test on macos - Minor cleanup --- cpython-tests/3.12/test_zipfile/test_core.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/cpython-tests/3.12/test_zipfile/test_core.py b/cpython-tests/3.12/test_zipfile/test_core.py index 8e52321..2b60aae 100644 --- a/cpython-tests/3.12/test_zipfile/test_core.py +++ b/cpython-tests/3.12/test_zipfile/test_core.py @@ -10,8 +10,9 @@ import sys import time import unittest -import unittest.mock as mock import zipfile +import unittest.mock as mock +from test.support import os_helper from tempfile import TemporaryFile @@ -50,11 +51,12 @@ def get_files(test): test.assertFalse(f.closed) class AbstractTestsWithSourceFile: + @classmethod def setUpClass(cls): - cls.line_gen = [bytes("Zipfile test line %d. random float: %f\n" % - (i, random()), "ascii") - for i in range(FIXEDTEST_SIZE)] + cls.line_gen = [ + bytes("Zipfile test line %d. random float: %f\n" % (i, random()), "ascii") + for i in range(FIXEDTEST_SIZE)] cls.data = b''.join(cls.line_gen) def setUp(self): @@ -1556,12 +1558,18 @@ def open(self, path): def extractall(self, ar): ar.extractall(self.testdir) + @os_helper.skip_unless_symlink def test_overwrite_file_symlink_as_file(self): self.skipTest('Zipfile2 will overwrite symlinks with a real file') + @os_helper.skip_unless_symlink def test_overwrite_broken_file_symlink_as_file(self): self.skipTest('Zipfile2 will overwrite symlinks with a real file') + @unittest.skipIf(sys.platform == 'darwin', 'FIXME: not sure why this test fails') + def test_overwrite_dir_as_file(self): + super().test_overwrite_dir_as_file() + class OtherTests(unittest.TestCase): def test_open_via_zip_info(self): From 6f550141e318d36c172a8570f5df2e7a432fb0ba Mon Sep 17 00:00:00 2001 From: Ioannis Tziakos Date: Sat, 15 Feb 2025 13:57:10 +0000 Subject: [PATCH 29/41] Update intergration testing for Python 3.8.x --- cpython-tests/3.8/test_zipfile.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/cpython-tests/3.8/test_zipfile.py b/cpython-tests/3.8/test_zipfile.py index 60574e0..754904a 100644 --- a/cpython-tests/3.8/test_zipfile.py +++ b/cpython-tests/3.8/test_zipfile.py @@ -1510,15 +1510,17 @@ def _test_extract_hackers_arcnames(self, hacknames): class OtherTests(unittest.TestCase): + def test_open_via_zip_info(self): # Create the ZIP archive - with zipfile2.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED) as zipfp: + with zipfile2.ZipFile( + TESTFN2, "w", zipfile.ZIP_STORED, low_level=True) as zipfp: zipfp.writestr("name", "foo") with self.assertWarns(UserWarning): zipfp.writestr("name", "bar") self.assertEqual(zipfp.namelist(), ["name"] * 2) - with zipfile2.ZipFile(TESTFN2, "r") as zipfp: + with zipfile2.ZipFile(TESTFN2, "r", low_level=True) as zipfp: infos = zipfp.infolist() data = b"" for info in infos: @@ -2029,6 +2031,7 @@ def test_full_overlap(self): with self.assertRaisesRegex(zipfile.BadZipFile, 'File name.*differ'): zipf.read('b') + @unittest.skipIf(sys.version_info < (3, 8, 19), "Behaviour not supported in Python < 3.8.19") @requires_zlib def test_quoted_overlap(self): data = ( From 79abb9ba336971a7aed6b800dea842050194c40a Mon Sep 17 00:00:00 2001 From: Ioannis Tziakos Date: Sat, 15 Feb 2025 14:05:58 +0000 Subject: [PATCH 30/41] Fix python 3.9 integration testing and minor cleanup --- cpython-tests/3.8/test_zipfile.py | 2 +- cpython-tests/3.9/test_zipfile.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/cpython-tests/3.8/test_zipfile.py b/cpython-tests/3.8/test_zipfile.py index 754904a..ce00f13 100644 --- a/cpython-tests/3.8/test_zipfile.py +++ b/cpython-tests/3.8/test_zipfile.py @@ -2031,8 +2031,8 @@ def test_full_overlap(self): with self.assertRaisesRegex(zipfile.BadZipFile, 'File name.*differ'): zipf.read('b') - @unittest.skipIf(sys.version_info < (3, 8, 19), "Behaviour not supported in Python < 3.8.19") @requires_zlib + @unittest.skipIf(sys.version_info < (3, 8, 19), "Behaviour not supported in Python < 3.8.19") def test_quoted_overlap(self): data = ( b'PK\x03\x04\x14\x00\x00\x00\x08\x00\xa0lH\x05Y\xfc' diff --git a/cpython-tests/3.9/test_zipfile.py b/cpython-tests/3.9/test_zipfile.py index a4a8e73..79ddf3f 100644 --- a/cpython-tests/3.9/test_zipfile.py +++ b/cpython-tests/3.9/test_zipfile.py @@ -2078,6 +2078,7 @@ def test_full_overlap(self): zipf.read('b') @requires_zlib() + @unittest.skipIf(sys.version_info < (3, 9, 19), "Behaviour not supported in Python < 3.9.19") def test_quoted_overlap(self): data = ( b'PK\x03\x04\x14\x00\x00\x00\x08\x00\xa0lH\x05Y\xfc' From 4e9a64cc01584fe223e37ba0f2ccb4f7d7ff94c6 Mon Sep 17 00:00:00 2001 From: Ioannis Tziakos Date: Sat, 15 Feb 2025 14:12:25 +0000 Subject: [PATCH 31/41] Update integration tests for Python 3.10.x skip test on < 3.10.14 since the zipfile module does not have that behaviour --- cpython-tests/3.10/test_zipfile.py | 1 + 1 file changed, 1 insertion(+) diff --git a/cpython-tests/3.10/test_zipfile.py b/cpython-tests/3.10/test_zipfile.py index f0c47cb..7f34502 100644 --- a/cpython-tests/3.10/test_zipfile.py +++ b/cpython-tests/3.10/test_zipfile.py @@ -2092,6 +2092,7 @@ def test_full_overlap(self): zipf.read('b') @requires_zlib() + @unittest.skipIf(sys.version_info < (3, 10, 14), "Behaviour not supported in Python < 3.10.14") def test_quoted_overlap(self): data = ( b'PK\x03\x04\x14\x00\x00\x00\x08\x00\xa0lH\x05Y\xfc' From 88786e566356f21d653b407b504dd60cd76fe14f Mon Sep 17 00:00:00 2001 From: Ioannis Tziakos Date: Sat, 22 Feb 2025 10:14:20 +0000 Subject: [PATCH 32/41] use unittest for the integration tests --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 223c176..5f7c125 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -26,7 +26,7 @@ jobs: - name: Test with haas run: python -m haas zipfile2 - name: Integration tests - run: python -m haas . + run: python -m unittest discover . working-directory: cpython-tests/${{ matrix.python-version }} code-lint: From 790e3853008b024afa314498d3e194f77c154270 Mon Sep 17 00:00:00 2001 From: Ioannis Tziakos Date: Sat, 22 Feb 2025 10:25:45 +0000 Subject: [PATCH 33/41] Update test.yml --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 5f7c125..cbb07f2 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -26,7 +26,7 @@ jobs: - name: Test with haas run: python -m haas zipfile2 - name: Integration tests - run: python -m unittest discover . + run: python -m unittest discover working-directory: cpython-tests/${{ matrix.python-version }} code-lint: From fa61d73fafe586230718e5bcb5d54268730d77b3 Mon Sep 17 00:00:00 2001 From: Ioannis Tziakos Date: Sat, 22 Feb 2025 10:43:02 +0000 Subject: [PATCH 34/41] Skip tests for Python versions that where not fixed --- cpython-tests/3.10/test_zipfile.py | 1 + cpython-tests/3.8/test_zipfile.py | 2 ++ cpython-tests/3.9/test_zipfile.py | 1 + 3 files changed, 4 insertions(+) diff --git a/cpython-tests/3.10/test_zipfile.py b/cpython-tests/3.10/test_zipfile.py index 7f34502..c21f313 100644 --- a/cpython-tests/3.10/test_zipfile.py +++ b/cpython-tests/3.10/test_zipfile.py @@ -3284,6 +3284,7 @@ def test_extract_orig_with_implied_dirs(self, alpharep): zipfile.Path(zf) zf.extractall(source_path.parent) + @unittest.skipIf(sys.version_info < (3, 10, 15), "Issue fixed in Python >= 3.9.15") def test_malformed_paths(self): """ Path should handle malformed paths gracefully. diff --git a/cpython-tests/3.8/test_zipfile.py b/cpython-tests/3.8/test_zipfile.py index ce00f13..176d592 100644 --- a/cpython-tests/3.8/test_zipfile.py +++ b/cpython-tests/3.8/test_zipfile.py @@ -3012,6 +3012,8 @@ def test_implied_dirs_performance(self): data = ['/'.join(string.ascii_lowercase + str(n)) for n in range(10000)] zipfile.CompleteDirs._implied_dirs(data) + @requires_zlib + @unittest.skipIf(sys.version_info < (3, 8, 20), "Issue fixed in Python >= 3.8.20") def test_malformed_paths(self): """ Path should handle malformed paths gracefully. diff --git a/cpython-tests/3.9/test_zipfile.py b/cpython-tests/3.9/test_zipfile.py index 79ddf3f..3f7f834 100644 --- a/cpython-tests/3.9/test_zipfile.py +++ b/cpython-tests/3.9/test_zipfile.py @@ -3058,6 +3058,7 @@ def test_implied_dirs_performance(self): data = ['/'.join(string.ascii_lowercase + str(n)) for n in range(10000)] zipfile.CompleteDirs._implied_dirs(data) + @unittest.skipIf(sys.version_info < (3, 9, 20), "Issue fixed in Python >= 3.9.20") def test_malformed_paths(self): """ Path should handle malformed paths gracefully. From 7d728a3990d8f7985009ca6a400e26364cb02828 Mon Sep 17 00:00:00 2001 From: Ioannis Tziakos Date: Sat, 22 Feb 2025 10:45:49 +0000 Subject: [PATCH 35/41] Record which checkout was used for the integration tests --- cpython-tests/README.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/cpython-tests/README.rst b/cpython-tests/README.rst index 1113129..5a39e81 100644 --- a/cpython-tests/README.rst +++ b/cpython-tests/README.rst @@ -2,3 +2,9 @@ Zipfile test code copied from the cpython source code. Adapted to test against the zipfile2. Each folder corresponds to the tests from a specific python version. + +- 3.8 (checkout out at 3.8.20) +- 3.9 (checkout out at 3.9.20) +- 3.10 (checkout out at 3.10.16) +- 3.11 (checkout out at 3.11.10) +- 3.12 (checkout out at 3.12.6) From 325082b46c07e20cfacbb2af0ab2b14172c1873c Mon Sep 17 00:00:00 2001 From: Ioannis Tziakos Date: Sat, 22 Feb 2025 10:48:02 +0000 Subject: [PATCH 36/41] Update test.yml --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index cbb07f2..223c176 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -26,7 +26,7 @@ jobs: - name: Test with haas run: python -m haas zipfile2 - name: Integration tests - run: python -m unittest discover + run: python -m haas . working-directory: cpython-tests/${{ matrix.python-version }} code-lint: From 53031ddfea74b59a7560086b290a11eed53bc719 Mon Sep 17 00:00:00 2001 From: Ioannis Tziakos Date: Sat, 22 Feb 2025 11:16:11 +0000 Subject: [PATCH 37/41] Update intergration test checkouts --- cpython-tests/3.12/test_zipfile/test_core.py | 295 ++++++++++++++++++- cpython-tests/README.rst | 6 +- 2 files changed, 288 insertions(+), 13 deletions(-) diff --git a/cpython-tests/3.12/test_zipfile/test_core.py b/cpython-tests/3.12/test_zipfile/test_core.py index 2b60aae..9626aef 100644 --- a/cpython-tests/3.12/test_zipfile/test_core.py +++ b/cpython-tests/3.12/test_zipfile/test_core.py @@ -1,3 +1,4 @@ +import _pyio import array import contextlib import importlib.util @@ -10,9 +11,8 @@ import sys import time import unittest -import zipfile import unittest.mock as mock -from test.support import os_helper +import zipfile from tempfile import TemporaryFile @@ -51,12 +51,11 @@ def get_files(test): test.assertFalse(f.closed) class AbstractTestsWithSourceFile: - @classmethod def setUpClass(cls): - cls.line_gen = [ - bytes("Zipfile test line %d. random float: %f\n" % (i, random()), "ascii") - for i in range(FIXEDTEST_SIZE)] + cls.line_gen = [bytes("Zipfile test line %d. random float: %f\n" % + (i, random()), "ascii") + for i in range(FIXEDTEST_SIZE)] cls.data = b''.join(cls.line_gen) def setUp(self): @@ -885,9 +884,10 @@ def test_append(self): self.assertEqual(zinfo.extra, extra) def make_zip64_file( - self, file_size_64_set=False, file_size_extra=False, - compress_size_64_set=False, compress_size_extra=False, - header_offset_64_set=False, header_offset_extra=False,): + self, file_size_64_set=False, file_size_extra=False, + compress_size_64_set=False, compress_size_extra=False, + header_offset_64_set=False, header_offset_extra=False, + ): """Generate bytes sequence for a zip with (incomplete) zip64 data. The actual values (not the zip 64 0xffffffff values) stored in the file @@ -1191,7 +1191,7 @@ def test_unseekable_zip_known_filesize(self): # in as a zip, this test looks at the raw bytes created to ensure that # the correct data has been generated. # The spec for this can be found at: https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT - # The relevent sections for this test are: + # The relevant sections for this test are: # - 4.3.7 for local file header # - 4.3.9 for the data descriptor # - 4.5.3 for zip64 extra field @@ -1337,6 +1337,199 @@ class Bzip2WriterTests(AbstractWriterTests, unittest.TestCase): class LzmaWriterTests(AbstractWriterTests, unittest.TestCase): compression = zipfile.ZIP_LZMA +@unittest.skip('Zipfile2 does not support PyZipFile') +class PyZipFileTests(unittest.TestCase): + def assertCompiledIn(self, name, namelist): + if name + 'o' not in namelist: + self.assertIn(name + 'c', namelist) + + def requiresWriteAccess(self, path): + # effective_ids unavailable on windows + if not os.access(path, os.W_OK, + effective_ids=os.access in os.supports_effective_ids): + self.skipTest('requires write access to the installed location') + filename = os.path.join(path, 'test_zipfile.try') + try: + fd = os.open(filename, os.O_WRONLY | os.O_CREAT) + os.close(fd) + except Exception: + self.skipTest('requires write access to the installed location') + unlink(filename) + + def test_write_pyfile(self): + self.requiresWriteAccess(os.path.dirname(__file__)) + with TemporaryFile() as t, zipfile.PyZipFile(t, "w") as zipfp: + fn = __file__ + if fn.endswith('.pyc'): + path_split = fn.split(os.sep) + if os.altsep is not None: + path_split.extend(fn.split(os.altsep)) + if '__pycache__' in path_split: + fn = importlib.util.source_from_cache(fn) + else: + fn = fn[:-1] + + zipfp.writepy(fn) + + bn = os.path.basename(fn) + self.assertNotIn(bn, zipfp.namelist()) + self.assertCompiledIn(bn, zipfp.namelist()) + + with TemporaryFile() as t, zipfile.PyZipFile(t, "w") as zipfp: + fn = __file__ + if fn.endswith('.pyc'): + fn = fn[:-1] + + zipfp.writepy(fn, "testpackage") + + bn = "%s/%s" % ("testpackage", os.path.basename(fn)) + self.assertNotIn(bn, zipfp.namelist()) + self.assertCompiledIn(bn, zipfp.namelist()) + + def test_write_python_package(self): + import email + packagedir = os.path.dirname(email.__file__) + self.requiresWriteAccess(packagedir) + + with TemporaryFile() as t, zipfile.PyZipFile(t, "w") as zipfp: + zipfp.writepy(packagedir) + + # Check for a couple of modules at different levels of the + # hierarchy + names = zipfp.namelist() + self.assertCompiledIn('email/__init__.py', names) + self.assertCompiledIn('email/mime/text.py', names) + + def test_write_filtered_python_package(self): + import test + packagedir = os.path.dirname(test.__file__) + self.requiresWriteAccess(packagedir) + + with TemporaryFile() as t, zipfile.PyZipFile(t, "w") as zipfp: + + # first make sure that the test folder gives error messages + # (on the badsyntax_... files) + with captured_stdout() as reportSIO: + zipfp.writepy(packagedir) + reportStr = reportSIO.getvalue() + self.assertTrue('SyntaxError' in reportStr) + + # then check that the filter works on the whole package + with captured_stdout() as reportSIO: + zipfp.writepy(packagedir, filterfunc=lambda whatever: False) + reportStr = reportSIO.getvalue() + self.assertTrue('SyntaxError' not in reportStr) + + # then check that the filter works on individual files + def filter(path): + return not os.path.basename(path).startswith("bad") + with captured_stdout() as reportSIO, self.assertWarns(UserWarning): + zipfp.writepy(packagedir, filterfunc=filter) + reportStr = reportSIO.getvalue() + if reportStr: + print(reportStr) + self.assertTrue('SyntaxError' not in reportStr) + + def test_write_with_optimization(self): + import email + packagedir = os.path.dirname(email.__file__) + self.requiresWriteAccess(packagedir) + optlevel = 1 if __debug__ else 0 + ext = '.pyc' + + with TemporaryFile() as t, \ + zipfile.PyZipFile(t, "w", optimize=optlevel) as zipfp: + zipfp.writepy(packagedir) + + names = zipfp.namelist() + self.assertIn('email/__init__' + ext, names) + self.assertIn('email/mime/text' + ext, names) + + def test_write_python_directory(self): + os.mkdir(TESTFN2) + try: + with open(os.path.join(TESTFN2, "mod1.py"), "w", encoding='utf-8') as fp: + fp.write("print(42)\n") + + with open(os.path.join(TESTFN2, "mod2.py"), "w", encoding='utf-8') as fp: + fp.write("print(42 * 42)\n") + + with open(os.path.join(TESTFN2, "mod2.txt"), "w", encoding='utf-8') as fp: + fp.write("bla bla bla\n") + + with TemporaryFile() as t, zipfile.PyZipFile(t, "w") as zipfp: + zipfp.writepy(TESTFN2) + + names = zipfp.namelist() + self.assertCompiledIn('mod1.py', names) + self.assertCompiledIn('mod2.py', names) + self.assertNotIn('mod2.txt', names) + + finally: + rmtree(TESTFN2) + + def test_write_python_directory_filtered(self): + os.mkdir(TESTFN2) + try: + with open(os.path.join(TESTFN2, "mod1.py"), "w", encoding='utf-8') as fp: + fp.write("print(42)\n") + + with open(os.path.join(TESTFN2, "mod2.py"), "w", encoding='utf-8') as fp: + fp.write("print(42 * 42)\n") + + with TemporaryFile() as t, zipfile.PyZipFile(t, "w") as zipfp: + zipfp.writepy(TESTFN2, filterfunc=lambda fn: + not fn.endswith('mod2.py')) + + names = zipfp.namelist() + self.assertCompiledIn('mod1.py', names) + self.assertNotIn('mod2.py', names) + + finally: + rmtree(TESTFN2) + + def test_write_non_pyfile(self): + with TemporaryFile() as t, zipfile.PyZipFile(t, "w") as zipfp: + with open(TESTFN, 'w', encoding='utf-8') as f: + f.write('most definitely not a python file') + self.assertRaises(RuntimeError, zipfp.writepy, TESTFN) + unlink(TESTFN) + + def test_write_pyfile_bad_syntax(self): + os.mkdir(TESTFN2) + try: + with open(os.path.join(TESTFN2, "mod1.py"), "w", encoding='utf-8') as fp: + fp.write("Bad syntax in python file\n") + + with TemporaryFile() as t, zipfile.PyZipFile(t, "w") as zipfp: + # syntax errors are printed to stdout + with captured_stdout() as s: + zipfp.writepy(os.path.join(TESTFN2, "mod1.py")) + + self.assertIn("SyntaxError", s.getvalue()) + + # as it will not have compiled the python file, it will + # include the .py file not .pyc + names = zipfp.namelist() + self.assertIn('mod1.py', names) + self.assertNotIn('mod1.pyc', names) + + finally: + rmtree(TESTFN2) + + def test_write_pathlike(self): + os.mkdir(TESTFN2) + try: + with open(os.path.join(TESTFN2, "mod1.py"), "w", encoding='utf-8') as fp: + fp.write("print(42)\n") + + with TemporaryFile() as t, zipfile.PyZipFile(t, "w") as zipfp: + zipfp.writepy(FakePath(os.path.join(TESTFN2, "mod1.py"))) + names = zipfp.namelist() + self.assertCompiledIn('mod1.py', names) + finally: + rmtree(TESTFN2) + class ExtractTests(unittest.TestCase): @@ -3250,5 +3443,87 @@ def test_too_short(self): b"zzz", zipfile._strip_extra(b"zzz", (self.ZIP64_EXTRA,))) +class StatIO(_pyio.BytesIO): + """Buffer which remembers the number of bytes that were read.""" + + def __init__(self): + super().__init__() + self.bytes_read = 0 + + def read(self, size=-1): + bs = super().read(size) + self.bytes_read += len(bs) + return bs + + +class StoredZipExtFileRandomReadTest(unittest.TestCase): + """Tests whether an uncompressed, unencrypted zip entry can be randomly + seek and read without reading redundant bytes.""" + def test_stored_seek_and_read(self): + + sio = StatIO() + # 20000 bytes + txt = b'0123456789' * 2000 + + # The seek length must be greater than ZipExtFile.MIN_READ_SIZE + # as `ZipExtFile._read2()` reads in blocks of this size and we + # need to seek out of the buffered data + read_buffer_size = zipfile.ZipExtFile.MIN_READ_SIZE + self.assertGreaterEqual(10002, read_buffer_size) # for forward seek test + self.assertGreaterEqual(5003, read_buffer_size) # for backward seek test + # The read length must be less than MIN_READ_SIZE, since we assume that + # only 1 block is read in the test. + read_length = 100 + self.assertGreaterEqual(read_buffer_size, read_length) # for read() calls + + with zipfile2.ZipFile(sio, "w", compression=zipfile.ZIP_STORED) as zipf: + zipf.writestr("foo.txt", txt) + + # check random seek and read on a file + with zipfile2.ZipFile(sio, "r") as zipf: + with zipf.open("foo.txt", "r") as fp: + # Test this optimized read hasn't rewound and read from the + # start of the file (as in the case of the unoptimized path) + + # forward seek + old_count = sio.bytes_read + forward_seek_len = 10002 + current_pos = 0 + fp.seek(forward_seek_len, os.SEEK_CUR) + current_pos += forward_seek_len + self.assertEqual(fp.tell(), current_pos) + self.assertEqual(fp._left, fp._compress_left) + arr = fp.read(read_length) + current_pos += read_length + self.assertEqual(fp.tell(), current_pos) + self.assertEqual(arr, txt[current_pos - read_length:current_pos]) + self.assertEqual(fp._left, fp._compress_left) + read_count = sio.bytes_read - old_count + self.assertLessEqual(read_count, read_buffer_size) + + # backward seek + old_count = sio.bytes_read + backward_seek_len = 5003 + fp.seek(-backward_seek_len, os.SEEK_CUR) + current_pos -= backward_seek_len + self.assertEqual(fp.tell(), current_pos) + self.assertEqual(fp._left, fp._compress_left) + arr = fp.read(read_length) + current_pos += read_length + self.assertEqual(fp.tell(), current_pos) + self.assertEqual(arr, txt[current_pos - read_length:current_pos]) + self.assertEqual(fp._left, fp._compress_left) + read_count = sio.bytes_read - old_count + self.assertLessEqual(read_count, read_buffer_size) + + # eof flags test + fp.seek(0, os.SEEK_END) + fp.seek(12345, os.SEEK_SET) + current_pos = 12345 + arr = fp.read(read_length) + current_pos += read_length + self.assertEqual(arr, txt[current_pos - read_length:current_pos]) + + if __name__ == "__main__": unittest.main() diff --git a/cpython-tests/README.rst b/cpython-tests/README.rst index 5a39e81..3dea1d7 100644 --- a/cpython-tests/README.rst +++ b/cpython-tests/README.rst @@ -4,7 +4,7 @@ Each folder corresponds to the tests from a specific python version. - 3.8 (checkout out at 3.8.20) -- 3.9 (checkout out at 3.9.20) +- 3.9 (checkout out at 3.9.21) - 3.10 (checkout out at 3.10.16) -- 3.11 (checkout out at 3.11.10) -- 3.12 (checkout out at 3.12.6) +- 3.11 (checkout out at 3.11.11) +- 3.12 (checkout out at 3.12.9) From 3072ce85e17d1d0dd6ed0ad1cbe8e096ed46f331 Mon Sep 17 00:00:00 2001 From: Ioannis Tziakos Date: Sat, 22 Feb 2025 11:22:56 +0000 Subject: [PATCH 38/41] Add missing import --- cpython-tests/3.12/test_zipfile/test_core.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/cpython-tests/3.12/test_zipfile/test_core.py b/cpython-tests/3.12/test_zipfile/test_core.py index 9626aef..cff8412 100644 --- a/cpython-tests/3.12/test_zipfile/test_core.py +++ b/cpython-tests/3.12/test_zipfile/test_core.py @@ -27,7 +27,8 @@ captured_stdout, captured_stderr, requires_subprocess ) from test.support.os_helper import ( - TESTFN, unlink, rmtree, temp_dir, temp_cwd, fd_count, FakePath + TESTFN, unlink, rmtree, temp_dir, temp_cwd, fd_count, FakePath, + skip_unless_symlink ) @@ -1751,11 +1752,11 @@ def open(self, path): def extractall(self, ar): ar.extractall(self.testdir) - @os_helper.skip_unless_symlink + @skip_unless_symlink def test_overwrite_file_symlink_as_file(self): self.skipTest('Zipfile2 will overwrite symlinks with a real file') - @os_helper.skip_unless_symlink + @skip_unless_symlink def test_overwrite_broken_file_symlink_as_file(self): self.skipTest('Zipfile2 will overwrite symlinks with a real file') From 4576f03fd7fcd7d70e67dfac7b900fb51e40473b Mon Sep 17 00:00:00 2001 From: Ioannis Tziakos Date: Sat, 22 Feb 2025 11:27:04 +0000 Subject: [PATCH 39/41] Update test.yml --- .github/workflows/test.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 223c176..0a38797 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -9,6 +9,7 @@ on: jobs: tests: strategy: + max-parallel: 3 matrix: python-version: ['3.8', '3.9', '3.10', '3.11', '3.12'] os: [ubuntu-latest, windows-latest, macos-latest] From 4cef1f7ecb30f55911261aace30b6c0de2df3f07 Mon Sep 17 00:00:00 2001 From: Ioannis Tziakos Date: Sat, 22 Feb 2025 12:11:00 +0000 Subject: [PATCH 40/41] More conditional skips for 3.10.x differences --- cpython-tests/3.10/test_zipfile.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cpython-tests/3.10/test_zipfile.py b/cpython-tests/3.10/test_zipfile.py index c21f313..c527101 100644 --- a/cpython-tests/3.10/test_zipfile.py +++ b/cpython-tests/3.10/test_zipfile.py @@ -3284,7 +3284,7 @@ def test_extract_orig_with_implied_dirs(self, alpharep): zipfile.Path(zf) zf.extractall(source_path.parent) - @unittest.skipIf(sys.version_info < (3, 10, 15), "Issue fixed in Python >= 3.9.15") + @unittest.skipIf(sys.version_info < (3, 10, 15), "Issue fixed in Python >= 3.10.15") def test_malformed_paths(self): """ Path should handle malformed paths gracefully. @@ -3303,6 +3303,7 @@ def test_malformed_paths(self): assert list(map(str, root.iterdir())) == ['../'] assert root.joinpath('..').joinpath('parent.txt').read_bytes() == b'content' + @unittest.skipIf(sys.version_info < (3, 10, 15), "Issue fixed in Python >= 3.10.15") def test_unsupported_names(self): """ Path segments with special characters are readable. @@ -3323,6 +3324,7 @@ def test_unsupported_names(self): assert item.name == 'V: NMS.flac', item.name assert root.joinpath('V: NMS.flac').read_bytes() == b"fLaC..." + @unittest.skipIf(sys.version_info < (3, 10, 15), "Issue fixed in Python >= 3.10.15") def test_backslash_not_separator(self): """ In a zip file, backslashes are not separators. From a9b43aa2b512759e5a5591575e1f459c0cdb9059 Mon Sep 17 00:00:00 2001 From: Ioannis Tziakos Date: Sat, 2 Aug 2025 10:48:40 +0100 Subject: [PATCH 41/41] Update github actions --- .github/workflows/test.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 0a38797..e28832c 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -15,9 +15,9 @@ jobs: os: [ubuntu-latest, windows-latest, macos-latest] runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - name: Install zipfile2 @@ -33,9 +33,9 @@ jobs: code-lint: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Python 3.8 - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: 3.8 - name: Install flake8