""" Java zip standard library APIs implemented in Python. java.util.zip package - ZipFile, ZipEntry, GZIP, CRC, Deflater, Inflater. """ from .._core import Stub from .io import IOException, Closeable, Flushable import zipfile as _zipfile import gzip as _gzip import zlib as _zlib import io as _pyio import os as _os from datetime import datetime as _datetime __all__ = [ "ZipFile", "ZipEntry", "ZipOutputStream", "ZipInputStream", "GZIPInputStream", "GZIPOutputStream", "CRC32", "CheckedInputStream", "CheckedOutputStream", "Adler32", "DataFormatException", "ZipException", "Deflater", "Inflater", "DeflaterOutputStream", "InflaterInputStream", ] # ============================================================ # Exception classes # ============================================================ class ZipException(IOException): pass class DataFormatException(Exception): pass # ============================================================ # CRC32 # ============================================================ class CRC32(Stub): def __init__(self): super().__init__() self._value = 0 def getValue(self): return self._value def reset(self): self._value = 0 def update(self, b, off=None, length=None): if isinstance(b, int): self._value = _zlib.crc32(bytes([b & 0xFF]), self._value) & 0xFFFFFFFF elif isinstance(b, (bytes, bytearray)): if off is not None and length is not None: self._value = _zlib.crc32(bytes(b[off:off+length]), self._value) & 0xFFFFFFFF else: self._value = _zlib.crc32(bytes(b), self._value) & 0xFFFFFFFF # ============================================================ # Adler32 # ============================================================ class Adler32(Stub): def __init__(self): super().__init__() self._value = 1 def getValue(self): return self._value def reset(self): self._value = 1 def update(self, b, off=None, length=None): if isinstance(b, int): self._value = _zlib.adler32(bytes([b & 0xFF]), self._value) & 0xFFFFFFFF elif isinstance(b, (bytes, bytearray)): if off is not None and length is not None: self._value = _zlib.adler32(bytes(b[off:off+length]), self._value) & 0xFFFFFFFF else: self._value = _zlib.adler32(bytes(b), self._value) & 0xFFFFFFFF # ============================================================ # CheckedInputStream / CheckedOutputStream # ============================================================ class CheckedInputStream(Closeable): def __init__(self, inp, cksum): super().__init__() self._in = inp self._cksum = cksum def read(self, b=None, off=None, length=None): if b is None: result = self._in.read() if result >= 0: self._cksum.update(result) return result if off is None: off = 0 if length is None: length = len(b) result = self._in.read(b, off, length) if result > 0: self._cksum.update(b[off:off+result]) return result def getChecksum(self): return self._cksum def close(self): self._in.close() def skip(self, n): return self._in.skip(n) class CheckedOutputStream(Closeable, Flushable): def __init__(self, out, cksum): super().__init__() self._out = out self._cksum = cksum def write(self, b, off=None, length=None): if isinstance(b, int): self._out.write(b) self._cksum.update(b) else: if off is not None and length is not None: data = bytes(b[off:off+length]) else: data = bytes(b) self._out.write(data) self._cksum.update(data) def getChecksum(self): return self._cksum def flush(self): self._out.flush() def close(self): self._out.close() # ============================================================ # ZipEntry # ============================================================ class ZipEntry(Stub): STORED = 0 DEFLATED = 8 def __init__(self, name): super().__init__() self._name = name self._method = ZipEntry.DEFLATED self._size = 0 self._compressed_size = 0 self._crc = 0 self._comment = "" self._time = 0 self._extra = b"" self._is_directory = name.endswith("/") def getName(self): return self._name def getMethod(self): return self._method def setMethod(self, method): self._method = method def getSize(self): return self._size def setSize(self, size): self._size = size def getCompressedSize(self): return self._compressed_size def setCompressedSize(self, size): self._compressed_size = size def getCrc(self): return self._crc def setCrc(self, crc): self._crc = crc def getComment(self): return self._comment def setComment(self, comment): self._comment = comment def getTime(self): return self._time def setTime(self, time): self._time = time def getExtra(self): return self._extra def setExtra(self, extra): self._extra = extra def isDirectory(self): return self._is_directory def toString(self): return self._name def __str__(self): return self._name # ============================================================ # ZipFile # ============================================================ class ZipFile(Closeable): OPEN_READ = 1 OPEN_DELETE = 2 def __init__(self, file, mode="r"): super().__init__() if isinstance(file, str): path = file else: path = file.getPath() self._path = path self._mode = mode try: self._zfile = _zipfile.ZipFile(path, mode) except Exception as e: raise ZipException(str(e)) def entries(self): return iter(self._zfile.infolist()) def getEntry(self, name): try: info = self._zfile.getinfo(name) entry = ZipEntry(info.filename) entry.setMethod(info.compress_type) entry.setSize(info.file_size) entry.setCompressedSize(info.compress_size) entry.setCrc(info.CRC) entry.setTime(int(_datetime(*info.date_time).timestamp() * 1000) if info.date_time else 0) return entry except KeyError: return None def getInputStream(self, entry): try: data = self._zfile.read(entry.getName() if isinstance(entry, ZipEntry) else entry) from ..io import ByteArrayInputStream return ByteArrayInputStream(data) except Exception as e: raise ZipException(str(e)) def close(self): self._zfile.close() def size(self): return len(self._zfile.namelist()) def getName(self): return self._path def getComment(self): return "" # ============================================================ # ZipOutputStream # ============================================================ class ZipOutputStream(Closeable, Flushable): STORED = 0 DEFLATED = 8 def __init__(self, out): super().__init__() self._out = out self._zfile = _zipfile.ZipFile(out, "w", _zipfile.ZIP_DEFLATED) self._method = ZipOutputStream.DEFLATED self._level = -1 self._comment = "" def putNextEntry(self, entry): zi = _zipfile.ZipInfo(entry.getName() if isinstance(entry, ZipEntry) else str(entry)) if hasattr(entry, 'getTime') and entry.getTime(): dt = _datetime.fromtimestamp(entry.getTime() / 1000.0) zi.date_time = dt.timetuple()[:6] self._zfile.writestr(zi, b"") def closeEntry(self): pass def write(self, b, off=None, length=None): if off is not None and length is not None: self._zfile._zout.write(bytes(b[off:off+length])) else: self._zfile._zout.write(bytes(b) if not isinstance(b, (bytes, bytearray)) else b) def finish(self): self._zfile.close() def close(self): self._zfile.close() def setMethod(self, method): self._method = method def setLevel(self, level): self._level = level def setComment(self, comment): self._comment = comment def flush(self): self._zfile._zout.flush() if hasattr(self._zfile._zout, 'flush') else None # ============================================================ # ZipInputStream # ============================================================ class ZipInputStream(Closeable): def __init__(self, inp): super().__init__() self._inp = inp self._zfile = _zipfile.ZipFile(inp) self._entries = iter(self._zfile.infolist()) self._current_entry = None self._current_data = b"" self._current_pos = 0 def getNextEntry(self): try: info = next(self._entries) entry = ZipEntry(info.filename) entry.setMethod(info.compress_type) entry.setSize(info.file_size) entry.setCompressedSize(info.compress_size) entry.setCrc(info.CRC) self._current_entry = info self._current_data = self._zfile.read(info) self._current_pos = 0 return entry except StopIteration: self._current_entry = None self._current_data = b"" return None def read(self, b=None, off=None, length=None): if self._current_entry is None: return -1 avail = len(self._current_data) - self._current_pos if avail <= 0: return -1 if b is None: val = self._current_data[self._current_pos] self._current_pos += 1 return val if off is None: off = 0 if length is None: length = len(b) n = min(length, avail) if isinstance(b, bytearray): b[off:off+n] = self._current_data[self._current_pos:self._current_pos+n] self._current_pos += n return n def closeEntry(self): self._current_entry = None self._current_data = b"" def available(self): if self._current_entry is None: return 0 return len(self._current_data) - self._current_pos def close(self): self._zfile.close() # ============================================================ # GZIPInputStream # ============================================================ class GZIPInputStream(Closeable): def __init__(self, inp, size=512): super().__init__() self._inp = inp self._gzip_file = _gzip.GzipFile(fileobj=_pyio.BufferedReader(inp) if hasattr(inp, 'read') else inp) def read(self, b=None, off=None, length=None): if b is None: data = self._gzip_file.read(1) return data[0] if data else -1 if off is None: off = 0 if length is None: length = len(b) data = self._gzip_file.read(length) if not data: return -1 n = len(data) if isinstance(b, bytearray): b[off:off+n] = data else: ba = bytearray(b) ba[off:off+n] = data b[:] = bytes(ba) return n def close(self): self._gzip_file.close() # ============================================================ # GZIPOutputStream # ============================================================ class GZIPOutputStream(Closeable, Flushable): def __init__(self, out, size=512): super().__init__() self._out = out self._gzip_file = _gzip.GzipFile(fileobj=out, mode="wb") def write(self, b, off=None, length=None): if isinstance(b, int): self._gzip_file.write(bytes([b & 0xFF])) elif isinstance(b, (bytes, bytearray)): if off is not None and length is not None: self._gzip_file.write(bytes(b[off:off+length])) else: self._gzip_file.write(bytes(b)) def finish(self): self._gzip_file.close() def close(self): self._gzip_file.close() def flush(self): self._gzip_file.flush() # ============================================================ # Deflater # ============================================================ class Deflater(Stub): BEST_COMPRESSION = 9 BEST_SPEED = 1 DEFAULT_COMPRESSION = -1 DEFAULT_STRATEGY = 0 FILTERED = 1 HUFFMAN_ONLY = 2 NO_COMPRESSION = 0 NO_FLUSH = 0 SYNC_FLUSH = 2 FULL_FLUSH = 3 def __init__(self, level=-1, nowrap=False): super().__init__() self._level = level self._nowrap = nowrap self._compress = _zlib.compressobj(level=level if level >= 0 else 6, method=_zlib.DEFLATED, wbits=-_zlib.MAX_WBITS if nowrap else _zlib.MAX_WBITS) self._input_buffer = b"" self._finished = False def setInput(self, data, off=0, length=-1): if length < 0: length = len(data) - off self._input_buffer = bytes(data[off:off+length]) def needsInput(self): return len(self._input_buffer) == 0 def setDictionary(self, dictionary): pass def getTotalIn(self): return len(self._input_buffer) def getTotalOut(self): return 0 def deflate(self, output, off=0, length=-1): if length < 0: length = len(output) - off data = self._compress.compress(self._input_buffer) self._input_buffer = b"" n = min(len(data), length) if isinstance(output, bytearray): output[off:off+n] = data[:n] else: ba = bytearray(output) ba[off:off+n] = data[:n] output[:] = bytes(ba) return n def finish(self): self._finished = True def finished(self): return self._finished def end(self): pass def getAdler(self): return 1 def reset(self): self._compress = _zlib.compressobj(level=self._level if self._level >= 0 else 6, method=_zlib.DEFLATED, wbits=-_zlib.MAX_WBITS if self._nowrap else _zlib.MAX_WBITS) self._input_buffer = b"" self._finished = False # ============================================================ # Inflater # ============================================================ class Inflater(Stub): def __init__(self, nowrap=False): super().__init__() self._nowrap = nowrap wbits = -_zlib.MAX_WBITS if nowrap else _zlib.MAX_WBITS self._decompress = _zlib.decompressobj(wbits) self._input_buffer = b"" self._finished = False def setInput(self, data, off=0, length=-1): if length < 0: length = len(data) - off self._input_buffer = bytes(data[off:off+length]) def needsInput(self): return len(self._input_buffer) == 0 def setDictionary(self, dictionary): pass def getTotalIn(self): return len(self._input_buffer) def getTotalOut(self): return 0 def inflate(self, output, off=0, length=-1): if length < 0: length = len(output) - off try: data = self._decompress.decompress(self._input_buffer) self._input_buffer = b"" n = min(len(data), length) if isinstance(output, bytearray): output[off:off+n] = data[:n] else: ba = bytearray(output) ba[off:off+n] = data[:n] output[:] = bytes(ba) return n except _zlib.error as e: raise DataFormatException(str(e)) def finished(self): return self._finished def end(self): pass def getAdler(self): return 1 def needsDictionary(self): return False def reset(self): wbits = -_zlib.MAX_WBITS if self._nowrap else _zlib.MAX_WBITS self._decompress = _zlib.decompressobj(wbits) self._input_buffer = b"" self._finished = False # ============================================================ # DeflaterOutputStream # ============================================================ class DeflaterOutputStream(Closeable, Flushable): def __init__(self, out, deflater=None, size=512): super().__init__() self._out = out self._deflater = deflater or Deflater() self._buffer = bytearray(size) def write(self, b, off=None, length=None): if isinstance(b, int): self._out.write(bytes([b & 0xFF])) else: if off is not None and length is not None: self._out.write(bytes(b[off:off+length])) else: self._deflater.setInput(bytes(b) if isinstance(b, (bytes, bytearray)) else bytes(b)) while not self._deflater.needsInput(): n = self._deflater.deflate(self._buffer) if n > 0: self._out.write(bytes(self._buffer[:n])) def finish(self): self._deflater.finish() while not self._deflater.finished(): n = self._deflater.deflate(self._buffer) if n > 0: self._out.write(bytes(self._buffer[:n])) def close(self): self.finish() self._deflater.end() self._out.close() def flush(self): self._out.flush() # ============================================================ # InflaterInputStream # ============================================================ class InflaterInputStream(Closeable): def __init__(self, inp, inflater=None, size=512): super().__init__() self._in = inp self._inflater = inflater or Inflater() self._buffer = bytearray(size) def read(self, b=None, off=None, length=None): if b is None: buf = bytearray(1) n = self._read_from_inflater(buf, 0, 1) return buf[0] if n > 0 else -1 if off is None: off = 0 if length is None: length = len(b) return self._read_from_inflater(b, off, length) def _read_from_inflater(self, b, off, length): while not self._inflater.finished(): if self._inflater.needsInput(): data = self._in.read(self._buffer) if data is None or data == -1 or data == 0: break if isinstance(data, int): break self._inflater.setInput(data) try: n = self._inflater.inflate(b, off, length) if n > 0: return n except DataFormatException: break return -1 def available(self): return 0 def close(self): self._inflater.end() self._in.close() def mark(self, readlimit): pass def reset(self): raise IOException("mark/reset not supported") def markSupported(self): return False