123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418 |
- """
- A helper module that can work with paths
- that can refer to data inside a zipfile
- XXX: Need to determine if isdir("zipfile.zip")
- should return True or False. Currently returns
- True, but that might do the wrong thing with
- data-files that are zipfiles.
- """
- import os as _os
- import zipfile as _zipfile
- import errno as _errno
- import time as _time
- import sys as _sys
- import stat as _stat
- _DFLT_DIR_MODE = (
- _stat.S_IXOTH
- | _stat.S_IXGRP
- | _stat.S_IXUSR
- | _stat.S_IROTH
- | _stat.S_IRGRP
- | _stat.S_IRUSR)
- _DFLT_FILE_MODE = (
- _stat.S_IROTH
- | _stat.S_IRGRP
- | _stat.S_IRUSR)
- if _sys.version_info[0] == 2:
- from StringIO import StringIO as _BaseStringIO
- from StringIO import StringIO as _BaseBytesIO
- class _StringIO (_BaseStringIO):
- def __enter__(self):
- return self
- def __exit__(self, exc_type, exc_value, traceback):
- self.close()
- return False
- class _BytesIO (_BaseBytesIO):
- def __enter__(self):
- return self
- def __exit__(self, exc_type, exc_value, traceback):
- self.close()
- return False
- else:
- from io import StringIO as _StringIO
- from io import BytesIO as _BytesIO
- def _locate(path):
- full_path = path
- if _os.path.exists(path):
- return path, None
- else:
- rest = []
- root = _os.path.splitdrive(path)
- while path and path != root:
- path, bn = _os.path.split(path)
- rest.append(bn)
- if _os.path.exists(path):
- break
- if path == root:
- raise IOError(
- _errno.ENOENT, full_path,
- "No such file or directory")
- if not _os.path.isfile(path):
- raise IOError(
- _errno.ENOENT, full_path,
- "No such file or directory")
- rest.reverse()
- return path, '/'.join(rest).strip('/')
- _open = open
- def open(path, mode='r'):
- if 'w' in mode or 'a' in mode:
- raise IOError(
- _errno.EINVAL, path, "Write access not supported")
- elif 'r+' in mode:
- raise IOError(
- _errno.EINVAL, path, "Write access not supported")
- full_path = path
- path, rest = _locate(path)
- if not rest:
- return _open(path, mode)
- else:
- try:
- zf = _zipfile.ZipFile(path, 'r')
- except _zipfile.error:
- raise IOError(
- _errno.ENOENT, full_path,
- "No such file or directory")
- try:
- data = zf.read(rest)
- except (_zipfile.error, KeyError):
- zf.close()
- raise IOError(
- _errno.ENOENT, full_path,
- "No such file or directory")
- zf.close()
- if mode == 'rb':
- return _BytesIO(data)
- else:
- if _sys.version_info[0] == 3:
- data = data.decode('ascii')
- return _StringIO(data)
- def listdir(path):
- full_path = path
- path, rest = _locate(path)
- if not rest and not _os.path.isfile(path):
- return _os.listdir(path)
- else:
- try:
- zf = _zipfile.ZipFile(path, 'r')
- except _zipfile.error:
- raise IOError(
- _errno.ENOENT, full_path,
- "No such file or directory")
- result = set()
- seen = False
- try:
- for nm in zf.namelist():
- if rest is None:
- seen = True
- value = nm.split('/')[0]
- if value:
- result.add(value)
- elif nm.startswith(rest):
- if nm == rest:
- seen = True
- value = ''
- pass
- elif nm[len(rest)] == '/':
- seen = True
- value = nm[len(rest)+1:].split('/')[0]
- else:
- value = None
- if value:
- result.add(value)
- except _zipfile.error:
- zf.close()
- raise IOError(
- _errno.ENOENT, full_path,
- "No such file or directory")
- zf.close()
- if not seen:
- raise IOError(
- _errno.ENOENT, full_path,
- "No such file or directory")
- return list(result)
- def isfile(path):
- full_path = path
- path, rest = _locate(path)
- if not rest:
- ok = _os.path.isfile(path)
- if ok:
- try:
- zf = _zipfile.ZipFile(path, 'r')
- return False
- except (_zipfile.error, IOError):
- return True
- return False
- zf = None
- try:
- zf = _zipfile.ZipFile(path, 'r')
- zf.getinfo(rest)
- zf.close()
- return True
- except (KeyError, _zipfile.error):
- if zf is not None:
- zf.close()
- # Check if this is a directory
- try:
- zf.getinfo(rest + '/')
- except KeyError:
- pass
- else:
- return False
- rest = rest + '/'
- for nm in zf.namelist():
- if nm.startswith(rest):
- # Directory
- return False
- # No trace in zipfile
- raise IOError(
- _errno.ENOENT, full_path,
- "No such file or directory")
- def isdir(path):
- full_path = path
- path, rest = _locate(path)
- if not rest:
- ok = _os.path.isdir(path)
- if not ok:
- try:
- zf = _zipfile.ZipFile(path, 'r')
- except (_zipfile.error, IOError):
- return False
- return True
- return True
- zf = None
- try:
- try:
- zf = _zipfile.ZipFile(path)
- except _zipfile.error:
- raise IOError(
- _errno.ENOENT, full_path,
- "No such file or directory")
- try:
- zf.getinfo(rest)
- except KeyError:
- pass
- else:
- # File found
- return False
- rest = rest + '/'
- try:
- zf.getinfo(rest)
- except KeyError:
- pass
- else:
- # Directory entry found
- return True
- for nm in zf.namelist():
- if nm.startswith(rest):
- return True
- raise IOError(
- _errno.ENOENT, full_path,
- "No such file or directory")
- finally:
- if zf is not None:
- zf.close()
- def islink(path):
- full_path = path
- path, rest = _locate(path)
- if not rest:
- return _os.path.islink(path)
- try:
- zf = _zipfile.ZipFile(path)
- except _zipfile.error:
- raise IOError(
- _errno.ENOENT, full_path,
- "No such file or directory")
- try:
- try:
- zf.getinfo(rest)
- except KeyError:
- pass
- else:
- # File
- return False
- rest += '/'
- try:
- zf.getinfo(rest)
- except KeyError:
- pass
- else:
- # Directory
- return False
- for nm in zf.namelist():
- if nm.startswith(rest):
- # Directory without listing
- return False
- raise IOError(
- _errno.ENOENT, full_path,
- "No such file or directory")
- finally:
- zf.close()
- def readlink(path):
- full_path = path
- path, rest = _locate(path)
- if rest:
- # No symlinks inside zipfiles
- raise OSError(
- _errno.ENOENT, full_path,
- "No such file or directory")
- return _os.readlink(path)
- def getmode(path):
- full_path = path
- path, rest = _locate(path)
- if not rest:
- return _stat.S_IMODE(_os.stat(path).st_mode)
- zf = None
- try:
- zf = _zipfile.ZipFile(path)
- info = None
- try:
- info = zf.getinfo(rest)
- except KeyError:
- pass
- if info is None:
- try:
- info = zf.getinfo(rest + '/')
- except KeyError:
- pass
- if info is None:
- rest = rest + '/'
- for nm in zf.namelist():
- if nm.startswith(rest):
- break
- else:
- raise IOError(
- _errno.ENOENT, full_path,
- "No such file or directory")
- # Directory exists, but has no entry of its own.
- return _DFLT_DIR_MODE
- # The mode is stored without file-type in external_attr.
- if (info.external_attr >> 16) != 0:
- return _stat.S_IMODE(info.external_attr >> 16)
- else:
- return _DFLT_FILE_MODE
- finally:
- if zf is not None:
- zf.close()
- def getmtime(path):
- full_path = path
- path, rest = _locate(path)
- if not rest:
- return _os.path.getmtime(path)
- zf = None
- try:
- zf = _zipfile.ZipFile(path)
- info = None
- try:
- info = zf.getinfo(rest)
- except KeyError:
- pass
- if info is None:
- try:
- info = zf.getinfo(rest + '/')
- except KeyError:
- pass
- if info is None:
- rest = rest + '/'
- for nm in zf.namelist():
- if nm.startswith(rest):
- break
- else:
- raise IOError(
- _errno.ENOENT, full_path,
- "No such file or directory")
- # Directory exists, but has no entry of its
- # own, fake mtime by using the timestamp of
- # the zipfile itself.
- return _os.path.getmtime(path)
- return _time.mktime(info.date_time + (0, 0, -1))
- finally:
- if zf is not None:
- zf.close()
|