From 85f322a0c03d4b2bc3827c005697ab856cc287dc Mon Sep 17 00:00:00 2001 From: Ioannis Tziakos Date: Sat, 3 Aug 2024 12:02:17 +0100 Subject: [PATCH 01/17] Fix flake8 errors --- zipfile2/_lean_zipfile.py | 110 ++++++++------------------- zipfile2/_zipfile.py | 28 ++----- zipfile2/common.py | 15 ---- zipfile2/tests/common.py | 7 +- zipfile2/tests/test__lean_zipfile.py | 6 +- zipfile2/tests/test__zipfile.py | 13 ++-- 6 files changed, 51 insertions(+), 128 deletions(-) diff --git a/zipfile2/_lean_zipfile.py b/zipfile2/_lean_zipfile.py index d0ca55d..0a6e1c4 100644 --- a/zipfile2/_lean_zipfile.py +++ b/zipfile2/_lean_zipfile.py @@ -1,81 +1,38 @@ -from __future__ import absolute_import - import struct -import sys - -from .common import PY2, BytesIO, string_types - -if PY2: - MAX_EXTRACT_VERSION = 63 - from zipfile import ( - BadZipfile as BadZipFile, - ZipExtFile, - ZipInfo, - _CD_COMMENT_LENGTH, - _CD_EXTRA_FIELD_LENGTH, - _CD_FILENAME_LENGTH, - _CD_LOCAL_HEADER_OFFSET, - _CD_SIGNATURE, - _ECD_LOCATION, - _ECD_OFFSET, - _ECD_SIGNATURE, - _ECD_SIZE, - _EndRecData, - _FH_EXTRA_FIELD_LENGTH, - _FH_FILENAME_LENGTH, - _FH_SIGNATURE, - sizeCentralDir, - sizeEndCentDir64, - sizeEndCentDir64Locator, - sizeFileHeader, - stringCentralDir, - stringEndArchive64, - stringFileHeader, - structCentralDir, - structFileHeader, - ) -else: - from zipfile import ( - BadZipFile, - MAX_EXTRACT_VERSION, - ZipExtFile, - ZipInfo, - _CD_COMMENT_LENGTH, - _CD_EXTRA_FIELD_LENGTH, - _CD_FILENAME_LENGTH, - _CD_LOCAL_HEADER_OFFSET, - _CD_SIGNATURE, - _ECD_LOCATION, - _ECD_OFFSET, - _ECD_SIGNATURE, - _ECD_SIZE, - _EndRecData, - _FH_EXTRA_FIELD_LENGTH, - _FH_FILENAME_LENGTH, - _FH_SIGNATURE, - sizeCentralDir, - sizeEndCentDir64, - sizeEndCentDir64Locator, - sizeFileHeader, - stringCentralDir, - stringEndArchive64, - stringFileHeader, - structCentralDir, - structFileHeader, - ) +from io import BytesIO +from zipfile import ( + BadZipFile, + MAX_EXTRACT_VERSION, + ZipExtFile, + ZipInfo, + _CD_COMMENT_LENGTH, + _CD_EXTRA_FIELD_LENGTH, + _CD_FILENAME_LENGTH, + _CD_LOCAL_HEADER_OFFSET, + _CD_SIGNATURE, + _ECD_LOCATION, + _ECD_OFFSET, + _ECD_SIGNATURE, + _ECD_SIZE, + _EndRecData, + _FH_EXTRA_FIELD_LENGTH, + _FH_FILENAME_LENGTH, + _FH_SIGNATURE, + sizeCentralDir, + sizeEndCentDir64, + sizeEndCentDir64Locator, + sizeFileHeader, + stringCentralDir, + stringEndArchive64, + stringFileHeader, + structCentralDir, + structFileHeader, +) from .common import TooManyFiles _UTF8_EXTENSION_FLAG = 0x800 -if sys.version_info[:2] < (2, 7): - class _ZipExtFile(ZipExtFile): - def __enter__(self): - return self - - def __exit__(self, *a, **kw): - self.close() - class LeanZipFile(object): """ A limited but memory efficient zipfile reader. @@ -247,12 +204,7 @@ def open(self, zinfo): 'File name in directory %r and header %r differ.' % (zinfo.orig_filename, fname)) - if sys.version_info[:2] < (2, 7): - return _ZipExtFile(zef_file, zinfo) - elif sys.version_info[:2] < (3, 4) and sys.platform == 'win32': - return ZipExtFile(zef_file, 'r', zinfo) - else: - return ZipExtFile(zef_file, 'r', zinfo, None, close_fileobj=False) + return ZipExtFile(zef_file, 'r', zinfo, None, close_fileobj=False) def read(self, zinfo_or_name): """Return file bytes (as a string) for the given ZipInfo object or @@ -269,7 +221,7 @@ def read(self, zinfo_or_name): Will raise an error if more than one member is found with the given name. """ - if isinstance(zinfo_or_name, string_types): + if isinstance(zinfo_or_name, str): candidates = list(self.get_zip_infos(zinfo_or_name)) if len(candidates) < 1: msg = "There is no item named {0!r} found in the archive" diff --git a/zipfile2/_zipfile.py b/zipfile2/_zipfile.py index 5123fa7..9a2f736 100644 --- a/zipfile2/_zipfile.py +++ b/zipfile2/_zipfile.py @@ -1,27 +1,24 @@ -from __future__ import absolute_import - import errno import os import shutil import stat import string -import sys import time import zipfile from .common import text_type -IS_ZIPFILE_OLD_STYLE_CLASS = sys.version_info[:3] < (2, 7, 4) ZIP_SOFTLINK_ATTRIBUTE_MAGIC = 0xA1ED0000 # Enum choices for Zipfile.extractall preserve_permissions argument PERMS_PRESERVE_NONE, PERMS_PRESERVE_SAFE, PERMS_PRESERVE_ALL = range(3) + # Use octal as it is the convention used in zipinfo.c (as found in e.g. apt-get # source unzip) -_UNX_IFMT = 0o170000 # Unix file type mask -_UNX_IFLNK = 0o120000 # Unix symbolic link +_UNX_IFMT = 0o170000 # Unix file type mask +_UNX_IFLNK = 0o120000 # Unix symbolic link def is_zipinfo_symlink(zip_info): @@ -53,10 +50,7 @@ def __init__(self, file, mode='r', compression=zipfile.ZIP_STORED, If False, will raise an error when adding an already existing archive. """ - if IS_ZIPFILE_OLD_STYLE_CLASS: - zipfile.ZipFile.__init__(self, file, mode, compression, allowZip64) - else: - super(ZipFile, self).__init__(file, mode, compression, allowZip64) + super(ZipFile, self).__init__(file, mode, compression, allowZip64) self.low_level = low_level @@ -158,10 +152,7 @@ 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: - if IS_ZIPFILE_OLD_STYLE_CLASS: - zipfile.ZipFile.write(self, filename, arcname, compress_type) - else: - super(ZipFile, self).write(filename, arcname, compress_type) + super(ZipFile, self).write(filename, arcname, compress_type) self._filenames_set.add(arcname) def writestr(self, zinfo_or_arcname, bytes, compress_type=None): @@ -173,11 +164,7 @@ def writestr(self, zinfo_or_arcname, bytes, compress_type=None): self._ensure_uniqueness(arcname) self._filenames_set.add(arcname) - if IS_ZIPFILE_OLD_STYLE_CLASS: - zipfile.ZipFile.writestr(self, zinfo_or_arcname, bytes) - else: - super(ZipFile, self).writestr(zinfo_or_arcname, bytes, - compress_type) + super(ZipFile, self).writestr(zinfo_or_arcname, bytes, compress_type) # Overriden so that ZipFile.extract* support softlink def _extract_member(self, member, targetpath, pwd, preserve_permissions): @@ -246,7 +233,8 @@ def _extract_member_to(self, member, arcname, targetpath, pwd, finally: source.close() - if preserve_permissions in (PERMS_PRESERVE_SAFE, PERMS_PRESERVE_ALL): + if preserve_permissions in ( + PERMS_PRESERVE_SAFE, PERMS_PRESERVE_ALL): if preserve_permissions == PERMS_PRESERVE_ALL: # preserve bits 0-11: sugrwxrwxrwx, this include # sticky bit, uid bit, gid bit diff --git a/zipfile2/common.py b/zipfile2/common.py index 69dc950..7dbaea0 100644 --- a/zipfile2/common.py +++ b/zipfile2/common.py @@ -1,20 +1,5 @@ -import sys import zipfile -PY2 = sys.version_info[0] == 2 - -if PY2: - import cStringIO - string_types = basestring, - text_type = unicode - BytesIO = cStringIO.StringIO -else: - import io - string_types = str, - text_type = str - BytesIO = io.BytesIO - - class TooManyFiles(zipfile.BadZipfile): pass diff --git a/zipfile2/tests/common.py b/zipfile2/tests/common.py index b1e6c38..c63a2af 100644 --- a/zipfile2/tests/common.py +++ b/zipfile2/tests/common.py @@ -3,15 +3,14 @@ import unittest HERE = os.path.dirname(__file__) - NOSE_EGG = os.path.join(HERE, "data", "nose.egg") VTK_EGG = os.path.join(HERE, "data", "vtk.egg") ZIP_WITH_SOFTLINK = os.path.join(HERE, "data", "zip_with_softlink.zip") -ZIP_WITH_DIRECTORY_SOFTLINK = os.path.join(HERE, "data", "zip_with_directory_softlink.zip") +ZIP_WITH_DIRECTORY_SOFTLINK = os.path.join( + HERE, "data", "zip_with_directory_softlink.zip") ZIP_WITH_PERMISSIONS = os.path.join(HERE, "data", "zip_with_permissions.zip") ZIP_WITH_SOFTLINK_AND_PERMISSIONS = os.path.join( - HERE, "data", "zip_with_softlink_and_permissions.zip" -) + HERE, "data", "zip_with_softlink_and_permissions.zip") NOSE_SPEC_DEPEND = """\ metadata_version = '1.1' diff --git a/zipfile2/tests/test__lean_zipfile.py b/zipfile2/tests/test__lean_zipfile.py index 99b1452..cc7df13 100644 --- a/zipfile2/tests/test__lean_zipfile.py +++ b/zipfile2/tests/test__lean_zipfile.py @@ -1,14 +1,12 @@ -import os.path import shutil -import sys import tempfile import unittest -from zipfile2 import TooManyFiles +from zipfile2 import TooManyFiles from .._lean_zipfile import LeanZipFile -from .common import NOSE_EGG, NOSE_SPEC_DEPEND, VTK_EGG, ZIP_WITH_SOFTLINK +from .common import NOSE_EGG, NOSE_SPEC_DEPEND class TestLeanZipFile(unittest.TestCase): diff --git a/zipfile2/tests/test__zipfile.py b/zipfile2/tests/test__zipfile.py index a3ad754..cf71fc1 100644 --- a/zipfile2/tests/test__zipfile.py +++ b/zipfile2/tests/test__zipfile.py @@ -12,14 +12,12 @@ from zipfile2 import ( - PERMS_PRESERVE_SAFE, PERMS_PRESERVE_ALL, ZipFile -) + PERMS_PRESERVE_SAFE, PERMS_PRESERVE_ALL, ZipFile) from ..common import PY2, string_types from .common import ( NOSE_EGG, VTK_EGG, ZIP_WITH_DIRECTORY_SOFTLINK, ZIP_WITH_SOFTLINK, ZIP_WITH_PERMISSIONS, ZIP_WITH_SOFTLINK_AND_PERMISSIONS, - skip_unless_symlink -) + skip_unless_symlink) HERE = os.path.dirname(__file__) @@ -528,8 +526,11 @@ def test_add_tree(self): zp.extractall(extract_dir) self.assertCountEqual( zp.namelist(), - ["from/foo/", "from/foo/fubar/", "from/foo.txt", "from/foo/fubar/foo.txt"] - ) + [ + "from/foo/", + "from/foo/fubar/", + "from/foo.txt", + "from/foo/fubar/foo.txt"]) for f in r_files: os.path.exists(f) From 4a3b6b1f1dbb3206c6a2a7a44f6a957ede7525e9 Mon Sep 17 00:00:00 2001 From: Ioannis Tziakos Date: Sat, 17 Aug 2024 11:05:49 +0100 Subject: [PATCH 02/17] Use setup.cfg for package building --- VERSION | 1 + setup.cfg | 25 ++++++++++++ setup.py | 120 +++--------------------------------------------------- 3 files changed, 31 insertions(+), 115 deletions(-) create mode 100644 VERSION create mode 100644 setup.cfg diff --git a/VERSION b/VERSION new file mode 100644 index 0000000..afaf360 --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +1.0.0 \ No newline at end of file diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..3363216 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,25 @@ +[metadata] +name = zipfile2 +version = file: VERSION +url = https://github.com/enthought/pywin32-ctypes +author = Enthought Inc. +author_email = info@enthough.com +description = A (partial) reimplementation of pywin32 using ctypes/cffi +long_description = file: README.rst, CHANGELOG.txt +license = BSD-3-Clause +license_files = file: LICENSE.txt +classifier = + Programming Language :: Python :: 3 + Programming Language :: Python :: 3.8 + Programming Language :: Python :: 3.9 + Programming Language :: Python :: 3.10 + Programming Language :: Python :: 3.11 + Programming Language :: Python :: 3.12 + +[options] +zip_safe = False +packages = find: +python_requires = >=3.8 + +[options.packages.find] +exclude = docs* diff --git a/setup.py b/setup.py index b379aa7..a880e65 100644 --- a/setup.py +++ b/setup.py @@ -1,118 +1,8 @@ -import os -import subprocess - +from pathlib import Path from setuptools import setup +HERE = Path(__file__).parent +version = (HERE / 'VERSION').read_text().strip() +filename = (HERE / 'win32ctypes' / 'version.py').write_text(f'__version__ = "{version}"\n') -MAJOR = 0 -MINOR = 0 -MICRO = 13 - -IS_RELEASED = False - -VERSION_INFO = (MAJOR, MINOR, MICRO) -VERSION = ".".join(str(i) for i in VERSION_INFO) - - -# Return the git revision as a string -def git_version(): - def _minimal_ext_cmd(cmd): - # construct minimal environment - env = {} - for k in ['SYSTEMROOT', 'PATH']: - v = os.environ.get(k) - if v is not None: - env[k] = v - # LANGUAGE is used on win32 - env['LANGUAGE'] = 'C' - env['LANG'] = 'C' - env['LC_ALL'] = 'C' - out = subprocess.Popen(cmd, stdout=subprocess.PIPE, env=env).communicate()[0] - return out - - try: - out = _minimal_ext_cmd(['git', 'rev-parse', 'HEAD']) - git_revision = out.strip().decode('ascii') - except OSError: - git_revision = "Unknown" - - try: - out = _minimal_ext_cmd(['git', 'rev-list', '--count', 'HEAD']) - git_count = out.strip().decode('ascii') - except OSError: - git_count = "0" - - return git_revision, git_count - - -def write_version_py(filename): - template = """\ -# THIS FILE IS GENERATED FROM ZIPFILE2 SETUP.PY -version = '{version}' -full_version = '{full_version}' -git_revision = '{git_revision}' -is_released = {is_released} - -if not is_released: - version = full_version -""" - version = full_version = VERSION - is_released = IS_RELEASED - - git_rev = "Unknown" - git_count = "0" - - if os.path.exists('.git'): - git_rev, git_count = git_version() - elif os.path.exists(filename): - # must be a source distribution, use existing version file - try: - from zipfile2._version import git_revision as git_rev - except ImportError: - raise ImportError("Unable to import git_revision. Try removing " - "zipfile2/_version.py and the build directory " - "before building.") - - if not is_released: - full_version += '.dev{0}+{1}'.format(git_count, git_rev[:7]) - - with open(filename, "wt") as fp: - fp.write(template.format(version=version, - full_version=full_version, - git_revision=git_rev, - is_released=is_released)) - - -def main(): - write_version_py("zipfile2/_version.py") - - from zipfile2 import __version__ - - setup( - name="zipfile2", - author="David Cournapeau", - author_email="cournape@gmail.com", - description="An improved ZipFile class.", - packages=[ - "zipfile2", - "zipfile2.tests", - ], - package_data={ - "zipfile2.tests": ["data/*.zip", "data/*egg"], - }, - version=__version__, - license="PSFL", - classifiers=[ - "Development Status :: 3 - Alpha", - "License :: OSI Approved :: Python Software Foundation License", - "Operating System :: OS Independent", - "Programming Language :: Python :: 2.6", - "Programming Language :: Python :: 2.7", - "Programming Language :: Python :: 3.3", - "Programming Language :: Python :: 3.4", - ] - ) - - -if __name__ == "__main__": - main() +setup() From 9b3851bd77428de7e07764dd8e420634c22873c1 Mon Sep 17 00:00:00 2001 From: Ioannis Tziakos Date: Sat, 17 Aug 2024 11:06:16 +0100 Subject: [PATCH 03/17] Fix bad copypasta --- setup.cfg | 10 +++++----- setup.py | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/setup.cfg b/setup.cfg index 3363216..e67094f 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,13 +1,13 @@ [metadata] name = zipfile2 version = file: VERSION -url = https://github.com/enthought/pywin32-ctypes -author = Enthought Inc. -author_email = info@enthough.com -description = A (partial) reimplementation of pywin32 using ctypes/cffi +url = https://github.com/itziakos/zipfile2 +author = Ioannis Tziakos +author_email = itziakos@gmail.com +description = An improved ZipFile class that may be used as a 100 % backward compatible replacement. long_description = file: README.rst, CHANGELOG.txt license = BSD-3-Clause -license_files = file: LICENSE.txt +license_files = file: LICENSE classifier = Programming Language :: Python :: 3 Programming Language :: Python :: 3.8 diff --git a/setup.py b/setup.py index a880e65..0748d74 100644 --- a/setup.py +++ b/setup.py @@ -3,6 +3,6 @@ HERE = Path(__file__).parent version = (HERE / 'VERSION').read_text().strip() -filename = (HERE / 'win32ctypes' / 'version.py').write_text(f'__version__ = "{version}"\n') +filename = (HERE / 'zipfile2' / '_version.py').write_text(f'__version__ = "{version}"\n') setup() From 16742a0f2d8bbfe3eb3cdc9959abe1ef5a8d8d56 Mon Sep 17 00:00:00 2001 From: Ioannis Tziakos Date: Sat, 17 Aug 2024 11:18:45 +0100 Subject: [PATCH 04/17] Update manifest --- MANIFEST.in | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/MANIFEST.in b/MANIFEST.in index 1aba38f..b984543 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1 +1,7 @@ include LICENSE +include CHANGELOG +include *.rst +include *.txt +include *.cfg +recursive-include docs *.bat *.py *.rst +include docs/Makefile From 29117d0c5cbdf0693b82530d8a093da7c7990481 Mon Sep 17 00:00:00 2001 From: Ioannis Tziakos Date: Sat, 17 Aug 2024 11:19:29 +0100 Subject: [PATCH 05/17] Cleanup __init__.py --- zipfile2/__init__.py | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/zipfile2/__init__.py b/zipfile2/__init__.py index f6f3766..12c1bad 100644 --- a/zipfile2/__init__.py +++ b/zipfile2/__init__.py @@ -1,26 +1,22 @@ from __future__ import absolute_import try: - from ._version import (full_version as __version__, - git_revision as __git_revision__, - is_released as __is_released__) + from ._version import __version__, except ImportError: - __version__ = __git_revision__ = "no-built" - __is_released__ = False + __version__ = "no-built" from .common import TooManyFiles from ._zipfile import ( - PERMS_PRESERVE_NONE, PERMS_PRESERVE_SAFE, PERMS_PRESERVE_ALL, ZipFile -) + PERMS_PRESERVE_NONE, PERMS_PRESERVE_SAFE, PERMS_PRESERVE_ALL, ZipFile) from ._lean_zipfile import LeanZipFile from zipfile import ( ZIP64_LIMIT, ZIP_DEFLATED, ZIP_FILECOUNT_LIMIT, ZIP_MAX_COMMENT, - ZIP_STORED, -) + ZIP_STORED) __all__ = [ - "__git_revision__", "__is_released__", "__version__", "LeanZipFile", - "TooManyFiles", "ZipFile", "PERMS_PRESERVE_NONE", "PERMS_PRESERVE_SAFE", - "PERMS_PRESERVE_ALL", "ZIP64_LIMIT", "ZIP_DEFLATED", "ZIP_FILECOUNT_LIMIT", - "ZIP_MAX_COMMENT", "ZIP_STORED", + "__version__", + "LeanZipFile", "TooManyFiles", "ZipFile", + "PERMS_PRESERVE_NONE", "PERMS_PRESERVE_SAFE", + "PERMS_PRESERVE_ALL", "ZIP64_LIMIT", "ZIP_DEFLATED", + "ZIP_FILECOUNT_LIMIT", "ZIP_MAX_COMMENT", "ZIP_STORED", ] From a02c04c7ddf88b9a3611db9b506a1f27aac75693 Mon Sep 17 00:00:00 2001 From: Ioannis Tziakos Date: Sat, 17 Aug 2024 11:30:06 +0100 Subject: [PATCH 06/17] This is a dev version --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index afaf360..f630051 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.0.0 \ No newline at end of file +1.0.0.dev \ No newline at end of file From adfccabae8377dc3ac1a3cd91467ee313a943cf7 Mon Sep 17 00:00:00 2001 From: Ioannis Tziakos Date: Sat, 17 Aug 2024 11:31:31 +0100 Subject: [PATCH 07/17] Fix typos --- zipfile2/__init__.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/zipfile2/__init__.py b/zipfile2/__init__.py index 12c1bad..ab155cb 100644 --- a/zipfile2/__init__.py +++ b/zipfile2/__init__.py @@ -1,7 +1,5 @@ -from __future__ import absolute_import - try: - from ._version import __version__, + from ._version import __version__ except ImportError: __version__ = "no-built" From b752c56d7032a0b7fd8ae1cc813fd9d9928c0738 Mon Sep 17 00:00:00 2001 From: Ioannis Tziakos Date: Sat, 17 Aug 2024 11:33:39 +0100 Subject: [PATCH 08/17] Fix flake8 errors --- setup.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 0748d74..8bb5fc8 100644 --- a/setup.py +++ b/setup.py @@ -3,6 +3,8 @@ HERE = Path(__file__).parent version = (HERE / 'VERSION').read_text().strip() -filename = (HERE / 'zipfile2' / '_version.py').write_text(f'__version__ = "{version}"\n') +filename = ( + HERE / 'zipfile2' / '_version.py').write_text( + f'__version__ = "{version}"\n') setup() From 893e4b2cf4649463e4f6f59e5077bdfb937d8f3e Mon Sep 17 00:00:00 2001 From: Ioannis Tziakos Date: Sat, 17 Aug 2024 11:44:28 +0100 Subject: [PATCH 09/17] Minor cleanup --- README.rst | 3 +-- setup.cfg | 2 +- setup.py | 5 ++--- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/README.rst b/README.rst index ef3fd5e..5cf7f39 100644 --- a/README.rst +++ b/README.rst @@ -1,10 +1,9 @@ -zipfile2 contains an improved ZipFile class that may be used as a 100 % +zipfile2 contains an improved ZipFile class that may be used as a backward compatible replacement. Improvements compared to upstream zipfile stdlib: * Handling of symlinks (read and write) -* Compatible 2.6 onwards (including 3.x), include context manager * Raises an exception by default when duplicate members are detected. * Special class `LeanZipFile` to avoid using too much memory when handling zip files with a large number of members. Contrary to the stdlib diff --git a/setup.cfg b/setup.cfg index e67094f..e8da60d 100644 --- a/setup.cfg +++ b/setup.cfg @@ -4,7 +4,7 @@ version = file: VERSION url = https://github.com/itziakos/zipfile2 author = Ioannis Tziakos author_email = itziakos@gmail.com -description = An improved ZipFile class that may be used as a 100 % backward compatible replacement. +description = An improved ZipFile class that may be used as a backward compatible replacement. long_description = file: README.rst, CHANGELOG.txt license = BSD-3-Clause license_files = file: LICENSE diff --git a/setup.py b/setup.py index 8bb5fc8..c2d4f77 100644 --- a/setup.py +++ b/setup.py @@ -3,8 +3,7 @@ HERE = Path(__file__).parent version = (HERE / 'VERSION').read_text().strip() -filename = ( - HERE / 'zipfile2' / '_version.py').write_text( - f'__version__ = "{version}"\n') +filename = (HERE / 'zipfile2' / '_version.py') +filename.write_text(f"__version__ = '{version}'\n") setup() From e4e86a33f21d33ff296bb4cf0e8b4a32f0090fe1 Mon Sep 17 00:00:00 2001 From: Ioannis Tziakos Date: Sat, 17 Aug 2024 11:51:44 +0100 Subject: [PATCH 10/17] remove use of string_types and text_type --- zipfile2/_zipfile.py | 5 +---- zipfile2/tests/test__zipfile.py | 7 +------ 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/zipfile2/_zipfile.py b/zipfile2/_zipfile.py index 9a2f736..cf2deb0 100644 --- a/zipfile2/_zipfile.py +++ b/zipfile2/_zipfile.py @@ -6,9 +6,6 @@ import time import zipfile -from .common import text_type - - ZIP_SOFTLINK_ATTRIBUTE_MAGIC = 0xA1ED0000 # Enum choices for Zipfile.extractall preserve_permissions argument @@ -201,7 +198,7 @@ def _extract_member_to(self, member, arcname, targetpath, pwd, if os.path.sep == '\\': # filter illegal characters on Windows illegal = ':<>|"?*' - if isinstance(arcname, text_type): + if isinstance(arcname, str): table = dict((ord(c), ord('_')) for c in illegal) else: table = string.maketrans(illegal, '_' * len(illegal)) diff --git a/zipfile2/tests/test__zipfile.py b/zipfile2/tests/test__zipfile.py index cf71fc1..0567361 100644 --- a/zipfile2/tests/test__zipfile.py +++ b/zipfile2/tests/test__zipfile.py @@ -13,7 +13,6 @@ from zipfile2 import ( PERMS_PRESERVE_SAFE, PERMS_PRESERVE_ALL, ZipFile) -from ..common import PY2, string_types from .common import ( NOSE_EGG, VTK_EGG, ZIP_WITH_DIRECTORY_SOFTLINK, ZIP_WITH_SOFTLINK, ZIP_WITH_PERMISSIONS, ZIP_WITH_SOFTLINK_AND_PERMISSIONS, @@ -54,7 +53,7 @@ def _compute_checksum(fp): break return m.hexdigest() - if isinstance(path, string_types): + if isinstance(path, str): with open(path, "rb") as fp: return _compute_checksum(fp) else: @@ -76,10 +75,6 @@ def tearDown(self): shutil.rmtree(self.tempdir2) shutil.rmtree(self.tempdir) - if PY2: - def assertCountEqual(self, first, second, msg=None): - return self.assertItemsEqual(first, second, msg) - def test_simple(self): # Given path = NOSE_EGG From 5ea38d6eaaa5c0d452b5dd73699486f79641b870 Mon Sep 17 00:00:00 2001 From: Ioannis Tziakos Date: Sat, 17 Aug 2024 12:18:04 +0100 Subject: [PATCH 11/17] Fix LeanZipFile support for Python 3.12 --- zipfile2/_lean_zipfile.py | 10 +++++++--- zipfile2/common.py | 3 +++ 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/zipfile2/_lean_zipfile.py b/zipfile2/_lean_zipfile.py index 0a6e1c4..e9af3bd 100644 --- a/zipfile2/_lean_zipfile.py +++ b/zipfile2/_lean_zipfile.py @@ -27,9 +27,10 @@ stringFileHeader, structCentralDir, structFileHeader, + crc32 ) -from .common import TooManyFiles +from .common import TooManyFiles, PY312 _UTF8_EXTENSION_FLAG = 0x800 @@ -121,6 +122,7 @@ def get_zip_infos(self, *filenames): if centdir[_CD_SIGNATURE] != stringCentralDir: raise BadZipFile("Bad magic number for central directory") filename = fp.read(centdir[_CD_FILENAME_LENGTH]) + orig_filename_crc = crc32(filename) flags = centdir[5] if flags & _UTF8_EXTENSION_FLAG: # UTF-8 file names extension @@ -144,8 +146,10 @@ def get_zip_infos(self, *filenames): x._raw_time = t x.date_time = ((d >> 9) + 1980, (d >> 5) & 0xF, d & 0x1F, t >> 11, (t >> 5) & 0x3F, (t & 0x1F) * 2) - - x._decodeExtra() + if PY312: + x._decodeExtra(orig_filename_crc) + else: + x._decodeExtra() x.header_offset = x.header_offset + concat # update total bytes read from central directory diff --git a/zipfile2/common.py b/zipfile2/common.py index 7dbaea0..a25645a 100644 --- a/zipfile2/common.py +++ b/zipfile2/common.py @@ -1,5 +1,8 @@ import zipfile +import platform +PYTHON_VERSION = tuple(map(int, platform.python_version_tuple())) +PY312 = PYTHON_VERSION >= (3, 12, 0) class TooManyFiles(zipfile.BadZipfile): pass From 6b21df1ad3d1e7c10d23260b5f27487c7dee26a4 Mon Sep 17 00:00:00 2001 From: Ioannis Tziakos Date: Sat, 17 Aug 2024 12:20:57 +0100 Subject: [PATCH 12/17] Fix flake8 errors --- zipfile2/common.py | 1 + 1 file changed, 1 insertion(+) diff --git a/zipfile2/common.py b/zipfile2/common.py index a25645a..709260c 100644 --- a/zipfile2/common.py +++ b/zipfile2/common.py @@ -4,5 +4,6 @@ PYTHON_VERSION = tuple(map(int, platform.python_version_tuple())) PY312 = PYTHON_VERSION >= (3, 12, 0) + class TooManyFiles(zipfile.BadZipfile): pass From c0c3f0c44dd20c1bc54a6c9bacc083a24be19c3f Mon Sep 17 00:00:00 2001 From: Ioannis Tziakos Date: Sat, 17 Aug 2024 12:26:36 +0100 Subject: [PATCH 13/17] More robust teardown for tests on windows --- zipfile2/tests/test__zipfile.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/zipfile2/tests/test__zipfile.py b/zipfile2/tests/test__zipfile.py index 0567361..9730f27 100644 --- a/zipfile2/tests/test__zipfile.py +++ b/zipfile2/tests/test__zipfile.py @@ -577,7 +577,12 @@ def setUp(self): os.remove(path) def tearDown(self): - shutil.rmtree(self.tempdir, onerror=handle_readonly) + # Windows rmtree might fail the first time + for _ in range(5): + try: + shutil.rmtree(self.tempdir, onerror=handle_readonly) + except Exception: + pass def test_extractall_preserve_none(self): umask = os.umask(0) From 2f7a1838d45b2d823e9f1a21fea4c9872a131ad6 Mon Sep 17 00:00:00 2001 From: Ioannis Tziakos Date: Sat, 17 Aug 2024 12:42:40 +0100 Subject: [PATCH 14/17] Factor out a function for repeat_rmtree --- zipfile2/tests/common.py | 10 ++++++++++ zipfile2/tests/test__zipfile.py | 13 ++++--------- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/zipfile2/tests/common.py b/zipfile2/tests/common.py index c63a2af..2e2c519 100644 --- a/zipfile2/tests/common.py +++ b/zipfile2/tests/common.py @@ -1,5 +1,6 @@ import os import sys +import shutil import unittest HERE = os.path.dirname(__file__) @@ -196,3 +197,12 @@ def skip_unless_symlink(test): ok = can_symlink() msg = "Requires functional symlink implementation" return test if ok else unittest.skip(msg)(test) + + +def repeat_rmtree(*args, **kwargs): + # Windows rmtree might fail the first time + for _ in range(5): + try: + shutil.rmtree(*args, **kwargs) + except Exception: + pass diff --git a/zipfile2/tests/test__zipfile.py b/zipfile2/tests/test__zipfile.py index 9730f27..b05c067 100644 --- a/zipfile2/tests/test__zipfile.py +++ b/zipfile2/tests/test__zipfile.py @@ -16,7 +16,7 @@ from .common import ( NOSE_EGG, VTK_EGG, ZIP_WITH_DIRECTORY_SOFTLINK, ZIP_WITH_SOFTLINK, ZIP_WITH_PERMISSIONS, ZIP_WITH_SOFTLINK_AND_PERMISSIONS, - skip_unless_symlink) + skip_unless_symlink, repeat_rmtree) HERE = os.path.dirname(__file__) @@ -72,8 +72,8 @@ def setUp(self): self.tempdir2 = tempfile.mkdtemp() def tearDown(self): - shutil.rmtree(self.tempdir2) - shutil.rmtree(self.tempdir) + repeat_rmtree(self.tempdir2) + repeat_rmtree(self.tempdir) def test_simple(self): # Given @@ -577,12 +577,7 @@ def setUp(self): os.remove(path) def tearDown(self): - # Windows rmtree might fail the first time - for _ in range(5): - try: - shutil.rmtree(self.tempdir, onerror=handle_readonly) - except Exception: - pass + repeat_rmtree(self.tempdir, onerror=handle_readonly) def test_extractall_preserve_none(self): umask = os.umask(0) From fc8c0bab1eebabe1fc3f779778ed3a360cf68a42 Mon Sep 17 00:00:00 2001 From: Ioannis Tziakos Date: Sat, 17 Aug 2024 12:45:31 +0100 Subject: [PATCH 15/17] Fix flake8 errors --- zipfile2/tests/test__zipfile.py | 1 - 1 file changed, 1 deletion(-) diff --git a/zipfile2/tests/test__zipfile.py b/zipfile2/tests/test__zipfile.py index b05c067..3920afe 100644 --- a/zipfile2/tests/test__zipfile.py +++ b/zipfile2/tests/test__zipfile.py @@ -2,7 +2,6 @@ import hashlib import os import os.path -import shutil import stat import sys import tempfile From 2a91124c05efa73526dad4fce4155a123dbb4113 Mon Sep 17 00:00:00 2001 From: Ioannis Tziakos Date: Sat, 17 Aug 2024 12:45:42 +0100 Subject: [PATCH 16/17] return early in repeat_rmtree if possible --- zipfile2/tests/common.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/zipfile2/tests/common.py b/zipfile2/tests/common.py index 2e2c519..2ff72b7 100644 --- a/zipfile2/tests/common.py +++ b/zipfile2/tests/common.py @@ -200,9 +200,11 @@ def skip_unless_symlink(test): def repeat_rmtree(*args, **kwargs): - # Windows rmtree might fail the first time - for _ in range(5): - try: - shutil.rmtree(*args, **kwargs) - except Exception: - pass + # Windows rmtree might fail the first time + for _ in range(5): + try: + shutil.rmtree(*args, **kwargs) + except Exception: + pass + else: + break From a1f5a868d2065823ee8d9511d8a215731ac52a88 Mon Sep 17 00:00:00 2001 From: Ioannis Tziakos Date: Sat, 17 Aug 2024 12:50:22 +0100 Subject: [PATCH 17/17] Use sys.version_info --- zipfile2/common.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/zipfile2/common.py b/zipfile2/common.py index 709260c..47f0694 100644 --- a/zipfile2/common.py +++ b/zipfile2/common.py @@ -1,8 +1,9 @@ +import sys import zipfile -import platform -PYTHON_VERSION = tuple(map(int, platform.python_version_tuple())) -PY312 = PYTHON_VERSION >= (3, 12, 0) + +PYTHON_VERSION = sys.version_info[:2] +PY312 = PYTHON_VERSION >= (3, 12) class TooManyFiles(zipfile.BadZipfile):