zipp.py 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. """
  2. >>> root = Path(getfixture('zipfile_abcde'))
  3. >>> a, b = root.iterdir()
  4. >>> a
  5. Path('abcde.zip', 'a.txt')
  6. >>> b
  7. Path('abcde.zip', 'b/')
  8. >>> b.name
  9. 'b'
  10. >>> c = b / 'c.txt'
  11. >>> c
  12. Path('abcde.zip', 'b/c.txt')
  13. >>> c.name
  14. 'c.txt'
  15. >>> c.read_text()
  16. 'content of c'
  17. >>> c.exists()
  18. True
  19. >>> (b / 'missing.txt').exists()
  20. False
  21. >>> str(c)
  22. 'abcde.zip/b/c.txt'
  23. """
  24. from __future__ import division
  25. import io
  26. import sys
  27. import posixpath
  28. import zipfile
  29. import operator
  30. import functools
  31. __metaclass__ = type
  32. class Path:
  33. __repr = '{self.__class__.__name__}({self.root.filename!r}, {self.at!r})'
  34. def __init__(self, root, at=''):
  35. self.root = root if isinstance(root, zipfile.ZipFile) \
  36. else zipfile.ZipFile(self._pathlib_compat(root))
  37. self.at = at
  38. @staticmethod
  39. def _pathlib_compat(path):
  40. """
  41. For path-like objects, convert to a filename for compatibility
  42. on Python 3.6.1 and earlier.
  43. """
  44. try:
  45. return path.__fspath__()
  46. except AttributeError:
  47. return str(path)
  48. @property
  49. def open(self):
  50. return functools.partial(self.root.open, self.at)
  51. @property
  52. def name(self):
  53. return posixpath.basename(self.at.rstrip('/'))
  54. def read_text(self, *args, **kwargs):
  55. with self.open() as strm:
  56. return io.TextIOWrapper(strm, *args, **kwargs).read()
  57. def read_bytes(self):
  58. with self.open() as strm:
  59. return strm.read()
  60. def _is_child(self, path):
  61. return posixpath.dirname(path.at.rstrip('/')) == self.at.rstrip('/')
  62. def _next(self, at):
  63. return Path(self.root, at)
  64. def is_dir(self):
  65. return not self.at or self.at.endswith('/')
  66. def is_file(self):
  67. return not self.is_dir()
  68. def exists(self):
  69. return self.at in self.root.namelist()
  70. def iterdir(self):
  71. if not self.is_dir():
  72. raise ValueError("Can't listdir a file")
  73. names = map(operator.attrgetter('filename'), self.root.infolist())
  74. subs = map(self._next, names)
  75. return filter(self._is_child, subs)
  76. def __str__(self):
  77. return posixpath.join(self.root.filename, self.at)
  78. def __repr__(self):
  79. return self.__repr.format(self=self)
  80. def __truediv__(self, add):
  81. add = self._pathlib_compat(add)
  82. next = posixpath.join(self.at, add)
  83. next_dir = posixpath.join(self.at, add, '')
  84. names = self.root.namelist()
  85. return self._next(
  86. next_dir if next not in names and next_dir in names else next
  87. )
  88. if sys.version_info < (3,):
  89. __div__ = __truediv__