mirror of
https://github.com/RustPython/RustPython.git
synced 2026-06-02 19:39:49 +09:00
Update zipfile from v3.14.3
This commit is contained in:
committed by
Jeong, YunWon
parent
3ec7b5c375
commit
000025e09c
116
Lib/zipfile/__init__.py
vendored
116
Lib/zipfile/__init__.py
vendored
@@ -31,10 +31,15 @@ try:
|
||||
except ImportError:
|
||||
lzma = None
|
||||
|
||||
try:
|
||||
from compression import zstd # We may need its compression method
|
||||
except ImportError:
|
||||
zstd = None
|
||||
|
||||
__all__ = ["BadZipFile", "BadZipfile", "error",
|
||||
"ZIP_STORED", "ZIP_DEFLATED", "ZIP_BZIP2", "ZIP_LZMA",
|
||||
"is_zipfile", "ZipInfo", "ZipFile", "PyZipFile", "LargeZipFile",
|
||||
"Path"]
|
||||
"ZIP_ZSTANDARD", "is_zipfile", "ZipInfo", "ZipFile", "PyZipFile",
|
||||
"LargeZipFile", "Path"]
|
||||
|
||||
class BadZipFile(Exception):
|
||||
pass
|
||||
@@ -58,12 +63,14 @@ ZIP_STORED = 0
|
||||
ZIP_DEFLATED = 8
|
||||
ZIP_BZIP2 = 12
|
||||
ZIP_LZMA = 14
|
||||
ZIP_ZSTANDARD = 93
|
||||
# Other ZIP compression methods not supported
|
||||
|
||||
DEFAULT_VERSION = 20
|
||||
ZIP64_VERSION = 45
|
||||
BZIP2_VERSION = 46
|
||||
LZMA_VERSION = 63
|
||||
ZSTANDARD_VERSION = 63
|
||||
# we recognize (but not necessarily support) all features up to that version
|
||||
MAX_EXTRACT_VERSION = 63
|
||||
|
||||
@@ -227,8 +234,19 @@ class _Extra(bytes):
|
||||
|
||||
def _check_zipfile(fp):
|
||||
try:
|
||||
if _EndRecData(fp):
|
||||
return True # file has correct magic number
|
||||
endrec = _EndRecData(fp)
|
||||
if endrec:
|
||||
if endrec[_ECD_ENTRIES_TOTAL] == 0 and endrec[_ECD_SIZE] == 0 and endrec[_ECD_OFFSET] == 0:
|
||||
return True # Empty zipfiles are still zipfiles
|
||||
elif endrec[_ECD_DISK_NUMBER] == endrec[_ECD_DISK_START]:
|
||||
# Central directory is on the same disk
|
||||
fp.seek(sum(_handle_prepended_data(endrec)))
|
||||
if endrec[_ECD_SIZE] >= sizeCentralDir:
|
||||
data = fp.read(sizeCentralDir) # CD is where we expect it to be
|
||||
if len(data) == sizeCentralDir:
|
||||
centdir = struct.unpack(structCentralDir, data) # CD is the right size
|
||||
if centdir[_CD_SIGNATURE] == stringCentralDir:
|
||||
return True # First central directory entry has correct magic number
|
||||
except OSError:
|
||||
pass
|
||||
return False
|
||||
@@ -241,7 +259,9 @@ def is_zipfile(filename):
|
||||
result = False
|
||||
try:
|
||||
if hasattr(filename, "read"):
|
||||
pos = filename.tell()
|
||||
result = _check_zipfile(fp=filename)
|
||||
filename.seek(pos)
|
||||
else:
|
||||
with open(filename, "rb") as fp:
|
||||
result = _check_zipfile(fp)
|
||||
@@ -249,6 +269,19 @@ def is_zipfile(filename):
|
||||
pass
|
||||
return result
|
||||
|
||||
def _handle_prepended_data(endrec, debug=0):
|
||||
size_cd = endrec[_ECD_SIZE] # bytes in central directory
|
||||
offset_cd = endrec[_ECD_OFFSET] # offset of central directory
|
||||
|
||||
# "concat" is zero, unless zip was concatenated to another file
|
||||
concat = endrec[_ECD_LOCATION] - size_cd - offset_cd
|
||||
|
||||
if debug > 2:
|
||||
inferred = concat + offset_cd
|
||||
print("given, inferred, offset", offset_cd, inferred, concat)
|
||||
|
||||
return offset_cd, concat
|
||||
|
||||
def _EndRecData64(fpin, offset, endrec):
|
||||
"""
|
||||
Read the ZIP64 end-of-archive records and use that to update endrec
|
||||
@@ -519,6 +552,8 @@ class ZipInfo:
|
||||
min_version = max(BZIP2_VERSION, min_version)
|
||||
elif self.compress_type == ZIP_LZMA:
|
||||
min_version = max(LZMA_VERSION, min_version)
|
||||
elif self.compress_type == ZIP_ZSTANDARD:
|
||||
min_version = max(ZSTANDARD_VERSION, min_version)
|
||||
|
||||
self.extract_version = max(min_version, self.extract_version)
|
||||
self.create_version = max(min_version, self.create_version)
|
||||
@@ -619,6 +654,28 @@ class ZipInfo:
|
||||
|
||||
return zinfo
|
||||
|
||||
def _for_archive(self, archive):
|
||||
"""Resolve suitable defaults from the archive.
|
||||
|
||||
Resolve the date_time, compression attributes, and external attributes
|
||||
to suitable defaults as used by :method:`ZipFile.writestr`.
|
||||
|
||||
Return self.
|
||||
"""
|
||||
# gh-91279: Set the SOURCE_DATE_EPOCH to a specific timestamp
|
||||
epoch = os.environ.get('SOURCE_DATE_EPOCH')
|
||||
get_time = int(epoch) if epoch else time.time()
|
||||
self.date_time = time.localtime(get_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
|
||||
|
||||
def is_dir(self):
|
||||
"""Return True if this archive member is a directory."""
|
||||
if self.filename.endswith('/'):
|
||||
@@ -758,6 +815,7 @@ compressor_names = {
|
||||
14: 'lzma',
|
||||
18: 'terse',
|
||||
19: 'lz77',
|
||||
93: 'zstd',
|
||||
97: 'wavpack',
|
||||
98: 'ppmd',
|
||||
}
|
||||
@@ -777,6 +835,10 @@ def _check_compression(compression):
|
||||
if not lzma:
|
||||
raise RuntimeError(
|
||||
"Compression requires the (missing) lzma module")
|
||||
elif compression == ZIP_ZSTANDARD:
|
||||
if not zstd:
|
||||
raise RuntimeError(
|
||||
"Compression requires the (missing) compression.zstd module")
|
||||
else:
|
||||
raise NotImplementedError("That compression method is not supported")
|
||||
|
||||
@@ -793,6 +855,8 @@ def _get_compressor(compress_type, compresslevel=None):
|
||||
# compresslevel is ignored for ZIP_LZMA
|
||||
elif compress_type == ZIP_LZMA:
|
||||
return LZMACompressor()
|
||||
elif compress_type == ZIP_ZSTANDARD:
|
||||
return zstd.ZstdCompressor(level=compresslevel)
|
||||
else:
|
||||
return None
|
||||
|
||||
@@ -807,6 +871,8 @@ def _get_decompressor(compress_type):
|
||||
return bz2.BZ2Decompressor()
|
||||
elif compress_type == ZIP_LZMA:
|
||||
return LZMADecompressor()
|
||||
elif compress_type == ZIP_ZSTANDARD:
|
||||
return zstd.ZstdDecompressor()
|
||||
else:
|
||||
descr = compressor_names.get(compress_type)
|
||||
if descr:
|
||||
@@ -1326,7 +1392,8 @@ class ZipFile:
|
||||
mode: The mode can be either read 'r', write 'w', exclusive create 'x',
|
||||
or append 'a'.
|
||||
compression: ZIP_STORED (no compression), ZIP_DEFLATED (requires zlib),
|
||||
ZIP_BZIP2 (requires bz2) or ZIP_LZMA (requires lzma).
|
||||
ZIP_BZIP2 (requires bz2), ZIP_LZMA (requires lzma), or
|
||||
ZIP_ZSTANDARD (requires compression.zstd).
|
||||
allowZip64: if True ZipFile will create files with ZIP64 extensions when
|
||||
needed, otherwise it will raise an exception when this would
|
||||
be necessary.
|
||||
@@ -1335,6 +1402,9 @@ class ZipFile:
|
||||
When using ZIP_STORED or ZIP_LZMA this keyword has no effect.
|
||||
When using ZIP_DEFLATED integers 0 through 9 are accepted.
|
||||
When using ZIP_BZIP2 integers 1 through 9 are accepted.
|
||||
When using ZIP_ZSTANDARD integers -7 though 22 are common,
|
||||
see the CompressionParameter enum in compression.zstd for
|
||||
details.
|
||||
|
||||
"""
|
||||
|
||||
@@ -1468,21 +1538,17 @@ class ZipFile:
|
||||
raise BadZipFile("File is not a zip file")
|
||||
if self.debug > 1:
|
||||
print(endrec)
|
||||
size_cd = endrec[_ECD_SIZE] # bytes in central directory
|
||||
offset_cd = endrec[_ECD_OFFSET] # offset of central directory
|
||||
self._comment = endrec[_ECD_COMMENT] # archive comment
|
||||
|
||||
# "concat" is zero, unless zip was concatenated to another file
|
||||
concat = endrec[_ECD_LOCATION] - size_cd - offset_cd
|
||||
offset_cd, concat = _handle_prepended_data(endrec, self.debug)
|
||||
|
||||
if self.debug > 2:
|
||||
inferred = concat + offset_cd
|
||||
print("given, inferred, offset", offset_cd, inferred, concat)
|
||||
# self.start_dir: Position of start of central directory
|
||||
self.start_dir = offset_cd + concat
|
||||
|
||||
if self.start_dir < 0:
|
||||
raise BadZipFile("Bad offset for central directory")
|
||||
fp.seek(self.start_dir, 0)
|
||||
size_cd = endrec[_ECD_SIZE]
|
||||
data = fp.read(size_cd)
|
||||
fp = io.BytesIO(data)
|
||||
total = 0
|
||||
@@ -1771,8 +1837,8 @@ class ZipFile:
|
||||
def extract(self, member, path=None, pwd=None):
|
||||
"""Extract a member from the archive to the current working directory,
|
||||
using its full name. Its file information is extracted as accurately
|
||||
as possible. `member' may be a filename or a ZipInfo object. You can
|
||||
specify a different directory using `path'. You can specify the
|
||||
as possible. 'member' may be a filename or a ZipInfo object. You can
|
||||
specify a different directory using 'path'. You can specify the
|
||||
password to decrypt the file using 'pwd'.
|
||||
"""
|
||||
if path is None:
|
||||
@@ -1784,8 +1850,8 @@ class ZipFile:
|
||||
|
||||
def extractall(self, path=None, members=None, pwd=None):
|
||||
"""Extract all members from the archive to the current working
|
||||
directory. `path' specifies a different directory to extract to.
|
||||
`members' is optional and must be a subset of the list returned
|
||||
directory. 'path' specifies a different directory to extract to.
|
||||
'members' is optional and must be a subset of the list returned
|
||||
by namelist(). You can specify the password to decrypt all files
|
||||
using 'pwd'.
|
||||
"""
|
||||
@@ -1929,18 +1995,10 @@ class ZipFile:
|
||||
the name of the file in the archive."""
|
||||
if isinstance(data, str):
|
||||
data = data.encode("utf-8")
|
||||
if not isinstance(zinfo_or_arcname, ZipInfo):
|
||||
zinfo = ZipInfo(filename=zinfo_or_arcname,
|
||||
date_time=time.localtime(time.time())[:6])
|
||||
zinfo.compress_type = self.compression
|
||||
zinfo.compress_level = self.compresslevel
|
||||
if zinfo.filename.endswith('/'):
|
||||
zinfo.external_attr = 0o40775 << 16 # drwxrwxr-x
|
||||
zinfo.external_attr |= 0x10 # MS-DOS directory flag
|
||||
else:
|
||||
zinfo.external_attr = 0o600 << 16 # ?rw-------
|
||||
else:
|
||||
if isinstance(zinfo_or_arcname, ZipInfo):
|
||||
zinfo = zinfo_or_arcname
|
||||
else:
|
||||
zinfo = ZipInfo(zinfo_or_arcname)._for_archive(self)
|
||||
|
||||
if not self.fp:
|
||||
raise ValueError(
|
||||
@@ -2059,6 +2117,8 @@ class ZipFile:
|
||||
min_version = max(BZIP2_VERSION, min_version)
|
||||
elif zinfo.compress_type == ZIP_LZMA:
|
||||
min_version = max(LZMA_VERSION, min_version)
|
||||
elif zinfo.compress_type == ZIP_ZSTANDARD:
|
||||
min_version = max(ZSTANDARD_VERSION, min_version)
|
||||
|
||||
extract_version = max(min_version, zinfo.extract_version)
|
||||
create_version = max(min_version, zinfo.create_version)
|
||||
@@ -2301,7 +2361,7 @@ def main(args=None):
|
||||
import argparse
|
||||
|
||||
description = 'A simple command-line interface for zipfile module.'
|
||||
parser = argparse.ArgumentParser(description=description)
|
||||
parser = argparse.ArgumentParser(description=description, color=True)
|
||||
group = parser.add_mutually_exclusive_group(required=True)
|
||||
group.add_argument('-l', '--list', metavar='<zipfile>',
|
||||
help='Show listing of a zipfile')
|
||||
|
||||
2
Lib/zipfile/_path/__init__.py
vendored
2
Lib/zipfile/_path/__init__.py
vendored
@@ -281,7 +281,7 @@ class Path:
|
||||
>>> str(path.parent)
|
||||
'mem'
|
||||
|
||||
If the zipfile has no filename, such attributes are not
|
||||
If the zipfile has no filename, such attributes are not
|
||||
valid and accessing them will raise an Exception.
|
||||
|
||||
>>> zf.filename = None
|
||||
|
||||
4
Lib/zipfile/_path/glob.py
vendored
4
Lib/zipfile/_path/glob.py
vendored
@@ -36,9 +36,9 @@ class Translator:
|
||||
Apply '(?s:)' to create a non-matching group that
|
||||
matches newlines (valid on Unix).
|
||||
|
||||
Append '\Z' to imply fullmatch even when match is used.
|
||||
Append '\z' to imply fullmatch even when match is used.
|
||||
"""
|
||||
return rf'(?s:{pattern})\Z'
|
||||
return rf'(?s:{pattern})\z'
|
||||
|
||||
def match_dirs(self, pattern):
|
||||
"""
|
||||
|
||||
Reference in New Issue
Block a user