mirror of
https://github.com/python/cpython.git
synced 2025-09-26 18:29:57 +00:00
Issue #26039: Added zipfile.ZipInfo.from_file() and zipinfo.ZipInfo.is_dir().
Patch by Thomas Kluyver.
This commit is contained in:
parent
46988d3659
commit
503f908090
5 changed files with 100 additions and 29 deletions
|
@ -465,6 +465,22 @@ Instances of the :class:`ZipInfo` class are returned by the :meth:`.getinfo` and
|
||||||
:meth:`.infolist` methods of :class:`ZipFile` objects. Each object stores
|
:meth:`.infolist` methods of :class:`ZipFile` objects. Each object stores
|
||||||
information about a single member of the ZIP archive.
|
information about a single member of the ZIP archive.
|
||||||
|
|
||||||
|
There is one classmethod to make a :class:`ZipInfo` instance for a filesystem
|
||||||
|
file:
|
||||||
|
|
||||||
|
.. classmethod:: ZipInfo.from_file(filename, arcname=None)
|
||||||
|
|
||||||
|
Construct a :class:`ZipInfo` instance for a file on the filesystem, in
|
||||||
|
preparation for adding it to a zip file.
|
||||||
|
|
||||||
|
*filename* should be the path to a file or directory on the filesystem.
|
||||||
|
|
||||||
|
If *arcname* is specified, it is used as the name within the archive.
|
||||||
|
If *arcname* is not specified, the name will be the same as *filename*, but
|
||||||
|
with any drive letter and leading path separators removed.
|
||||||
|
|
||||||
|
.. versionadded:: 3.6
|
||||||
|
|
||||||
Instances have the following attributes:
|
Instances have the following attributes:
|
||||||
|
|
||||||
|
|
||||||
|
@ -574,3 +590,11 @@ Instances have the following attributes:
|
||||||
.. attribute:: ZipInfo.file_size
|
.. attribute:: ZipInfo.file_size
|
||||||
|
|
||||||
Size of the uncompressed file.
|
Size of the uncompressed file.
|
||||||
|
|
||||||
|
There is one method:
|
||||||
|
|
||||||
|
.. method:: ZipInfo.is_dir()
|
||||||
|
|
||||||
|
Return ``True`` if the ZipInfo represents a directory.
|
||||||
|
|
||||||
|
.. versionadded:: 3.6
|
||||||
|
|
|
@ -140,6 +140,16 @@ urllib.robotparser
|
||||||
(Contributed by Nikolay Bogoychev in :issue:`16099`.)
|
(Contributed by Nikolay Bogoychev in :issue:`16099`.)
|
||||||
|
|
||||||
|
|
||||||
|
zipfile
|
||||||
|
-------
|
||||||
|
|
||||||
|
A new :meth:`ZipInfo.from_file() <zipfile.ZipInfo.from_file>` class method
|
||||||
|
allow to make :class:`~zipfile.ZipInfo` instance from a filesystem file.
|
||||||
|
A new :meth:`ZipInfo.is_dir() <zipfile.ZipInfo.is_dir>` method can be used
|
||||||
|
to check if the :class:`~zipfile.ZipInfo` instance represents a directory.
|
||||||
|
(Contributed by Thomas Kluyver in :issue:`26039`.)
|
||||||
|
|
||||||
|
|
||||||
Optimizations
|
Optimizations
|
||||||
=============
|
=============
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ import io
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import importlib.util
|
import importlib.util
|
||||||
|
import posixpath
|
||||||
import time
|
import time
|
||||||
import struct
|
import struct
|
||||||
import zipfile
|
import zipfile
|
||||||
|
@ -2071,5 +2072,19 @@ class LzmaUniversalNewlineTests(AbstractUniversalNewlineTests,
|
||||||
unittest.TestCase):
|
unittest.TestCase):
|
||||||
compression = zipfile.ZIP_LZMA
|
compression = zipfile.ZIP_LZMA
|
||||||
|
|
||||||
|
class ZipInfoTests(unittest.TestCase):
|
||||||
|
def test_from_file(self):
|
||||||
|
zi = zipfile.ZipInfo.from_file(__file__)
|
||||||
|
self.assertEqual(posixpath.basename(zi.filename), 'test_zipfile.py')
|
||||||
|
self.assertFalse(zi.is_dir())
|
||||||
|
|
||||||
|
def test_from_dir(self):
|
||||||
|
dirpath = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
zi = zipfile.ZipInfo.from_file(dirpath, 'stdlib_tests')
|
||||||
|
self.assertEqual(zi.filename, 'stdlib_tests/')
|
||||||
|
self.assertTrue(zi.is_dir())
|
||||||
|
self.assertEqual(zi.compress_type, zipfile.ZIP_STORED)
|
||||||
|
self.assertEqual(zi.file_size, 0)
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
|
@ -371,7 +371,7 @@ class ZipInfo (object):
|
||||||
result.append(' filemode=%r' % stat.filemode(hi))
|
result.append(' filemode=%r' % stat.filemode(hi))
|
||||||
if lo:
|
if lo:
|
||||||
result.append(' external_attr=%#x' % lo)
|
result.append(' external_attr=%#x' % lo)
|
||||||
isdir = self.filename[-1:] == '/'
|
isdir = self.is_dir()
|
||||||
if not isdir or self.file_size:
|
if not isdir or self.file_size:
|
||||||
result.append(' file_size=%r' % self.file_size)
|
result.append(' file_size=%r' % self.file_size)
|
||||||
if ((not isdir or self.compress_size) and
|
if ((not isdir or self.compress_size) and
|
||||||
|
@ -469,6 +469,41 @@ class ZipInfo (object):
|
||||||
|
|
||||||
extra = extra[ln+4:]
|
extra = extra[ln+4:]
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_file(cls, filename, arcname=None):
|
||||||
|
"""Construct an appropriate ZipInfo for a file on the filesystem.
|
||||||
|
|
||||||
|
filename should be the path to a file or directory on the filesystem.
|
||||||
|
|
||||||
|
arcname is the name which it will have within the archive (by default,
|
||||||
|
this will be the same as filename, but without a drive letter and with
|
||||||
|
leading path separators removed).
|
||||||
|
"""
|
||||||
|
st = os.stat(filename)
|
||||||
|
isdir = stat.S_ISDIR(st.st_mode)
|
||||||
|
mtime = time.localtime(st.st_mtime)
|
||||||
|
date_time = mtime[0:6]
|
||||||
|
# Create ZipInfo instance to store file information
|
||||||
|
if arcname is None:
|
||||||
|
arcname = filename
|
||||||
|
arcname = os.path.normpath(os.path.splitdrive(arcname)[1])
|
||||||
|
while arcname[0] in (os.sep, os.altsep):
|
||||||
|
arcname = arcname[1:]
|
||||||
|
if isdir:
|
||||||
|
arcname += '/'
|
||||||
|
zinfo = cls(arcname, date_time)
|
||||||
|
zinfo.external_attr = (st.st_mode & 0xFFFF) << 16 # Unix attributes
|
||||||
|
if isdir:
|
||||||
|
zinfo.file_size = 0
|
||||||
|
zinfo.external_attr |= 0x10 # MS-DOS directory flag
|
||||||
|
else:
|
||||||
|
zinfo.file_size = st.st_size
|
||||||
|
|
||||||
|
return zinfo
|
||||||
|
|
||||||
|
def is_dir(self):
|
||||||
|
return self.filename[-1] == '/'
|
||||||
|
|
||||||
|
|
||||||
class _ZipDecrypter:
|
class _ZipDecrypter:
|
||||||
"""Class to handle decryption of files stored within a ZIP archive.
|
"""Class to handle decryption of files stored within a ZIP archive.
|
||||||
|
@ -1389,7 +1424,7 @@ class ZipFile:
|
||||||
if upperdirs and not os.path.exists(upperdirs):
|
if upperdirs and not os.path.exists(upperdirs):
|
||||||
os.makedirs(upperdirs)
|
os.makedirs(upperdirs)
|
||||||
|
|
||||||
if member.filename[-1] == '/':
|
if member.is_dir():
|
||||||
if not os.path.isdir(targetpath):
|
if not os.path.isdir(targetpath):
|
||||||
os.mkdir(targetpath)
|
os.mkdir(targetpath)
|
||||||
return targetpath
|
return targetpath
|
||||||
|
@ -1430,29 +1465,17 @@ class ZipFile:
|
||||||
raise RuntimeError(
|
raise RuntimeError(
|
||||||
"Attempt to write to ZIP archive that was already closed")
|
"Attempt to write to ZIP archive that was already closed")
|
||||||
|
|
||||||
st = os.stat(filename)
|
zinfo = ZipInfo.from_file(filename, arcname)
|
||||||
isdir = stat.S_ISDIR(st.st_mode)
|
|
||||||
mtime = time.localtime(st.st_mtime)
|
if zinfo.is_dir():
|
||||||
date_time = mtime[0:6]
|
zinfo.compress_size = 0
|
||||||
# Create ZipInfo instance to store file information
|
zinfo.CRC = 0
|
||||||
if arcname is None:
|
else:
|
||||||
arcname = filename
|
if compress_type is not None:
|
||||||
arcname = os.path.normpath(os.path.splitdrive(arcname)[1])
|
zinfo.compress_type = compress_type
|
||||||
while arcname[0] in (os.sep, os.altsep):
|
else:
|
||||||
arcname = arcname[1:]
|
zinfo.compress_type = self.compression
|
||||||
if isdir:
|
|
||||||
arcname += '/'
|
|
||||||
zinfo = ZipInfo(arcname, date_time)
|
|
||||||
zinfo.external_attr = (st[0] & 0xFFFF) << 16 # Unix attributes
|
|
||||||
if isdir:
|
|
||||||
zinfo.compress_type = ZIP_STORED
|
|
||||||
elif compress_type is None:
|
|
||||||
zinfo.compress_type = self.compression
|
|
||||||
else:
|
|
||||||
zinfo.compress_type = compress_type
|
|
||||||
|
|
||||||
zinfo.file_size = st.st_size
|
|
||||||
zinfo.flag_bits = 0x00
|
|
||||||
with self._lock:
|
with self._lock:
|
||||||
if self._seekable:
|
if self._seekable:
|
||||||
self.fp.seek(self.start_dir)
|
self.fp.seek(self.start_dir)
|
||||||
|
@ -1464,11 +1487,7 @@ class ZipFile:
|
||||||
self._writecheck(zinfo)
|
self._writecheck(zinfo)
|
||||||
self._didModify = True
|
self._didModify = True
|
||||||
|
|
||||||
if isdir:
|
if zinfo.is_dir():
|
||||||
zinfo.file_size = 0
|
|
||||||
zinfo.compress_size = 0
|
|
||||||
zinfo.CRC = 0
|
|
||||||
zinfo.external_attr |= 0x10 # MS-DOS directory flag
|
|
||||||
self.filelist.append(zinfo)
|
self.filelist.append(zinfo)
|
||||||
self.NameToInfo[zinfo.filename] = zinfo
|
self.NameToInfo[zinfo.filename] = zinfo
|
||||||
self.fp.write(zinfo.FileHeader(False))
|
self.fp.write(zinfo.FileHeader(False))
|
||||||
|
|
|
@ -170,6 +170,9 @@ Core and Builtins
|
||||||
Library
|
Library
|
||||||
-------
|
-------
|
||||||
|
|
||||||
|
- Issue #26039: Added zipfile.ZipInfo.from_file() and zipinfo.ZipInfo.is_dir().
|
||||||
|
Patch by Thomas Kluyver.
|
||||||
|
|
||||||
- Issue #12923: Reset FancyURLopener's redirect counter even if there is an
|
- Issue #12923: Reset FancyURLopener's redirect counter even if there is an
|
||||||
exception. Based on patches by Brian Brazil and Daniel Rocco.
|
exception. Based on patches by Brian Brazil and Daniel Rocco.
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue