| |
| from __future__ import with_statement |
| import logging |
| import os.path |
| from zipfile import ZIP_DEFLATED |
| from path import split as path_split |
| from path import get_ancestors as path_ancestors |
|
|
|
|
| logger = logging.getLogger(__name__) |
|
|
|
|
| def zipfile_nodes(zipfile): |
| seen = set() |
| for path in zipfile.namelist(): |
| for anc_path in path_ancestors(path): |
| if anc_path not in seen: |
| yield anc_path, ZipFileFolder(zipfile, anc_path) |
| seen.add(anc_path) |
| if path not in seen: |
| if path.endswith('/'): |
| yield path, ZipFileFolder(zipfile, path) |
| else: |
| yield path, ZipFileFile(zipfile, path) |
| seen.add(path) |
|
|
|
|
| class ZipFileNode(object): |
| |
| def __init__(self, zipfile, path): |
| self.zipfile = zipfile |
| self.path = path |
|
|
|
|
| class ZipFileFile(ZipFileNode): |
|
|
| def open(self, mode='r', compress_type=ZIP_DEFLATED): |
| if mode.startswith('r'): |
| return ZipFileStream(self.zipfile.open(self.path, mode)) |
| elif mode == 'w': |
| return ZipFileStream(ZipFileWritable(self.zipfile, self.path, |
| compress_type=compress_type)) |
|
|
| def put(self, filesystem_path, compress_type=ZIP_DEFLATED): |
| self.zipfile.write(filesystem_path, self.path, compress_type) |
|
|
|
|
| class ZipFileFolder(ZipFileNode): |
|
|
| def childs(self): |
| prefix = path_split(self.path) |
| prefix_len = len(prefix) |
| for path, node in zipfile_nodes(self.zipfile): |
| path_segments = path_split(path) |
| if len(path_segments) == prefix_len + 1: |
| if path_segments[:prefix_len] == prefix: |
| yield path_segments[-1], node |
|
|
| def __iter__(self): |
| for name, node in self.childs(): |
| yield name |
|
|
| def __getitem__(self, name): |
| for node_name, node in self.childs(): |
| if node_name == name: |
| return node |
| raise KeyError(name) |
|
|
| def file(self, name): |
| path = os.path.join(self.path, name) |
| return ZipFileFile(self.zipfile, path) |
|
|
| def folder(self, name): |
| path = os.path.join(self.path, name) |
| return ZipFileFolder(self.zipfile, path) |
|
|
|
|
| class ZipFileStorage(ZipFileFolder): |
|
|
| def __init__(self, *args, **kwargs): |
| import zipfile |
| self.zipfile = zipfile.ZipFile(*args, **kwargs) |
| self.path = '' |
|
|
| def childs(self): |
| for path, node in zipfile_nodes(self.zipfile): |
| path_segments = path_split(path) |
| if len(path_segments) == 1: |
| yield path_segments[-1], node |
|
|
| def close(self): |
| self.zipfile.close() |
|
|
| def __enter__(self): |
| return self |
|
|
| def __exit__(self, *args, **kwargs): |
| self.close() |
|
|
|
|
| class ZipFileStream(object): |
|
|
| def __init__(self, stream): |
| self.stream = stream |
|
|
| def __getattr__(self, name): |
| return getattr(self.stream, name) |
|
|
| def __enter__(self): |
| return self.stream |
|
|
| def __exit__(self, *args, **kwargs): |
| self.close() |
|
|
|
|
| class ZipFileWritable(object): |
|
|
| def __init__(self, zipfile, path, compress_type=ZIP_DEFLATED): |
| self.zipfile = zipfile |
| self.path = path |
| self.compress_type = compress_type |
|
|
| import tempfile |
| fd, tmp_path = tempfile.mkstemp() |
| self.tmp_f = os.fdopen(fd, 'w') |
| self.tmp_path = tmp_path |
|
|
| def __getattr__(self, name): |
| return getattr(self.tmp_f, name) |
|
|
| def close(self): |
| self.tmp_f.close() |
| self.zipfile.write(self.tmp_path, self.path, self.compress_type) |
| os.unlink(self.tmp_path) |
|
|