mirror of
https://github.com/python/cpython.git
synced 2025-09-26 18:29:57 +00:00
Issue #14099: Writing to ZipFile and reading multiple ZipExtFiles is
threadsafe now.
This commit is contained in:
parent
b67c516d8b
commit
f15e524026
2 changed files with 209 additions and 195 deletions
|
@ -13,6 +13,7 @@ import stat
|
||||||
import shutil
|
import shutil
|
||||||
import struct
|
import struct
|
||||||
import binascii
|
import binascii
|
||||||
|
import threading
|
||||||
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -647,12 +648,14 @@ def _get_decompressor(compress_type):
|
||||||
|
|
||||||
|
|
||||||
class _SharedFile:
|
class _SharedFile:
|
||||||
def __init__(self, file, pos, close):
|
def __init__(self, file, pos, close, lock):
|
||||||
self._file = file
|
self._file = file
|
||||||
self._pos = pos
|
self._pos = pos
|
||||||
self._close = close
|
self._close = close
|
||||||
|
self._lock = lock
|
||||||
|
|
||||||
def read(self, n=-1):
|
def read(self, n=-1):
|
||||||
|
with self._lock:
|
||||||
self._file.seek(self._pos)
|
self._file.seek(self._pos)
|
||||||
data = self._file.read(n)
|
data = self._file.read(n)
|
||||||
self._pos = self._file.tell()
|
self._pos = self._file.tell()
|
||||||
|
@ -990,6 +993,7 @@ class ZipFile:
|
||||||
self.fp = file
|
self.fp = file
|
||||||
self.filename = getattr(file, 'name', None)
|
self.filename = getattr(file, 'name', None)
|
||||||
self._fileRefCnt = 1
|
self._fileRefCnt = 1
|
||||||
|
self._lock = threading.RLock()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if mode == 'r':
|
if mode == 'r':
|
||||||
|
@ -1214,7 +1218,7 @@ class ZipFile:
|
||||||
zinfo = self.getinfo(name)
|
zinfo = self.getinfo(name)
|
||||||
|
|
||||||
self._fileRefCnt += 1
|
self._fileRefCnt += 1
|
||||||
zef_file = _SharedFile(self.fp, zinfo.header_offset, self._fpclose)
|
zef_file = _SharedFile(self.fp, zinfo.header_offset, self._fpclose, self._lock)
|
||||||
try:
|
try:
|
||||||
# Skip the file header:
|
# Skip the file header:
|
||||||
fheader = zef_file.read(sizeFileHeader)
|
fheader = zef_file.read(sizeFileHeader)
|
||||||
|
@ -1410,6 +1414,7 @@ class ZipFile:
|
||||||
|
|
||||||
zinfo.file_size = st.st_size
|
zinfo.file_size = st.st_size
|
||||||
zinfo.flag_bits = 0x00
|
zinfo.flag_bits = 0x00
|
||||||
|
with self._lock:
|
||||||
self.fp.seek(self.start_dir, 0)
|
self.fp.seek(self.start_dir, 0)
|
||||||
zinfo.header_offset = self.fp.tell() # Start of header bytes
|
zinfo.header_offset = self.fp.tell() # Start of header bytes
|
||||||
if zinfo.compress_type == ZIP_LZMA:
|
if zinfo.compress_type == ZIP_LZMA:
|
||||||
|
@ -1498,6 +1503,7 @@ class ZipFile:
|
||||||
"Attempt to write to ZIP archive that was already closed")
|
"Attempt to write to ZIP archive that was already closed")
|
||||||
|
|
||||||
zinfo.file_size = len(data) # Uncompressed size
|
zinfo.file_size = len(data) # Uncompressed size
|
||||||
|
with self._lock:
|
||||||
self.fp.seek(self.start_dir, 0)
|
self.fp.seek(self.start_dir, 0)
|
||||||
zinfo.header_offset = self.fp.tell() # Start of header data
|
zinfo.header_offset = self.fp.tell() # Start of header data
|
||||||
if compress_type is not None:
|
if compress_type is not None:
|
||||||
|
@ -1543,6 +1549,15 @@ class ZipFile:
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if self.mode in ("w", "a") and self._didModify: # write ending records
|
if self.mode in ("w", "a") and self._didModify: # write ending records
|
||||||
|
with self._lock:
|
||||||
|
self.fp.seek(self.start_dir, 0)
|
||||||
|
self._write_end_record()
|
||||||
|
finally:
|
||||||
|
fp = self.fp
|
||||||
|
self.fp = None
|
||||||
|
self._fpclose(fp)
|
||||||
|
|
||||||
|
def _write_end_record(self):
|
||||||
self.fp.seek(self.start_dir, 0)
|
self.fp.seek(self.start_dir, 0)
|
||||||
for zinfo in self.filelist: # write central directory
|
for zinfo in self.filelist: # write central directory
|
||||||
dt = zinfo.date_time
|
dt = zinfo.date_time
|
||||||
|
@ -1643,10 +1658,6 @@ class ZipFile:
|
||||||
self.fp.write(endrec)
|
self.fp.write(endrec)
|
||||||
self.fp.write(self._comment)
|
self.fp.write(self._comment)
|
||||||
self.fp.flush()
|
self.fp.flush()
|
||||||
finally:
|
|
||||||
fp = self.fp
|
|
||||||
self.fp = None
|
|
||||||
self._fpclose(fp)
|
|
||||||
|
|
||||||
def _fpclose(self, fp):
|
def _fpclose(self, fp):
|
||||||
assert self._fileRefCnt > 0
|
assert self._fileRefCnt > 0
|
||||||
|
|
|
@ -218,6 +218,9 @@ Core and Builtins
|
||||||
Library
|
Library
|
||||||
-------
|
-------
|
||||||
|
|
||||||
|
- Issue #14099: Writing to ZipFile and reading multiple ZipExtFiles is
|
||||||
|
threadsafe now.
|
||||||
|
|
||||||
- Issue #19361: JSON decoder now raises JSONDecodeError instead of ValueError.
|
- Issue #19361: JSON decoder now raises JSONDecodeError instead of ValueError.
|
||||||
|
|
||||||
- Issue #18518: timeit now rejects statements which can't be compiled outside
|
- Issue #18518: timeit now rejects statements which can't be compiled outside
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue