Kaydet (Commit) f15e5240 authored tarafından Serhiy Storchaka's avatar Serhiy Storchaka

Issue #14099: Writing to ZipFile and reading multiple ZipExtFiles is

threadsafe now.
üst b67c516d
...@@ -13,6 +13,7 @@ import stat ...@@ -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): ...@@ -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: ...@@ -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: ...@@ -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: ...@@ -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: ...@@ -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: ...@@ -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: ...@@ -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 ...@@ -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
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment