ZipPath: support hash, iterdir and proper / operator
This commit is contained in:
parent
e6e948de9c
commit
599a8b0dd7
2 changed files with 35 additions and 10 deletions
|
@ -6,7 +6,7 @@ from __future__ import annotations
|
||||||
import pathlib
|
import pathlib
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import sys
|
import sys
|
||||||
from typing import Union, IO, Sequence, Any
|
from typing import Union, IO, Sequence, Any, Iterator
|
||||||
import io
|
import io
|
||||||
|
|
||||||
PathIsh = Union[Path, str]
|
PathIsh = Union[Path, str]
|
||||||
|
@ -139,18 +139,22 @@ class ZipPath(ZipPathBase):
|
||||||
root: zipfile.ZipFile
|
root: zipfile.ZipFile
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def filename(self) -> str:
|
def filepath(self) -> Path:
|
||||||
res = self.root.filename
|
res = self.root.filename
|
||||||
assert res is not None # make mypy happy
|
assert res is not None # make mypy happy
|
||||||
return res
|
return Path(res)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def subpath(self) -> Path:
|
||||||
|
return Path(self.at)
|
||||||
|
|
||||||
def absolute(self) -> ZipPath:
|
def absolute(self) -> ZipPath:
|
||||||
return ZipPath(Path(self.filename).absolute(), self.at)
|
return ZipPath(self.filepath.absolute(), self.at)
|
||||||
|
|
||||||
def exists(self) -> bool:
|
def exists(self) -> bool:
|
||||||
if self.at == '':
|
if self.at == '':
|
||||||
# special case, the base class returns False in this case for some reason
|
# special case, the base class returns False in this case for some reason
|
||||||
return Path(self.filename).exists()
|
return self.filepath.exists()
|
||||||
return super().exists()
|
return super().exists()
|
||||||
|
|
||||||
def rglob(self, glob: str) -> Sequence[ZipPath]:
|
def rglob(self, glob: str) -> Sequence[ZipPath]:
|
||||||
|
@ -162,16 +166,25 @@ class ZipPath(ZipPathBase):
|
||||||
|
|
||||||
def relative_to(self, other: ZipPath) -> Path:
|
def relative_to(self, other: ZipPath) -> Path:
|
||||||
assert self.root == other.root, (self.root, other.root)
|
assert self.root == other.root, (self.root, other.root)
|
||||||
return Path(self.at).relative_to(Path(other.at))
|
return self.subpath.relative_to(other.subpath)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def parts(self) -> Sequence[str]:
|
def parts(self) -> Sequence[str]:
|
||||||
# messy, but might be ok..
|
# messy, but might be ok..
|
||||||
return Path(self.filename).parts + Path(self.at).parts
|
return self.filepath.parts + self.subpath.parts
|
||||||
|
|
||||||
|
def __truediv__(self, key) -> ZipPath:
|
||||||
|
# need to implement it so the return type is not zipfile.Path
|
||||||
|
s = super().__truediv__(key)
|
||||||
|
return ZipPath(s.root, s.at) # type: ignore[attr-defined]
|
||||||
|
|
||||||
|
def iterdir(self) -> Iterator[ZipPath]:
|
||||||
|
for s in super().iterdir():
|
||||||
|
yield ZipPath(s.root, s.at) # type: ignore[attr-defined]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def stem(self) -> str:
|
def stem(self) -> str:
|
||||||
return Path(self.at).stem
|
return self.subpath.stem
|
||||||
|
|
||||||
@property # type: ignore[misc]
|
@property # type: ignore[misc]
|
||||||
def __class__(self):
|
def __class__(self):
|
||||||
|
@ -181,4 +194,7 @@ class ZipPath(ZipPathBase):
|
||||||
# hmm, super class doesn't seem to treat as equals unless they are the same object
|
# hmm, super class doesn't seem to treat as equals unless they are the same object
|
||||||
if not isinstance(other, ZipPath):
|
if not isinstance(other, ZipPath):
|
||||||
return False
|
return False
|
||||||
return self.filename == other.filename and Path(self.at) == Path(other.at)
|
return (self.filepath, self.subpath) == (other.filepath, other.subpath)
|
||||||
|
|
||||||
|
def __hash__(self) -> int:
|
||||||
|
return hash((self.filepath, self.subpath))
|
||||||
|
|
|
@ -63,12 +63,17 @@ def test_zippath() -> None:
|
||||||
|
|
||||||
# magic! convenient to make third party libraries agnostic of ZipPath
|
# magic! convenient to make third party libraries agnostic of ZipPath
|
||||||
assert isinstance(zp, Path)
|
assert isinstance(zp, Path)
|
||||||
|
assert isinstance(zp, ZipPath)
|
||||||
|
assert isinstance(zp / 'subpath', Path)
|
||||||
# TODO maybe change __str__/__repr__? since it's a bit misleading:
|
# TODO maybe change __str__/__repr__? since it's a bit misleading:
|
||||||
# Path('/code/hpi/tests/core/structure_data/gdpr_export.zip', 'gdpr_export/')
|
# Path('/code/hpi/tests/core/structure_data/gdpr_export.zip', 'gdpr_export/')
|
||||||
|
|
||||||
assert ZipPath(target) == ZipPath(target)
|
assert ZipPath(target) == ZipPath(target)
|
||||||
assert zp.absolute() == zp
|
assert zp.absolute() == zp
|
||||||
|
|
||||||
|
# shouldn't crash
|
||||||
|
hash(zp)
|
||||||
|
|
||||||
assert zp.exists()
|
assert zp.exists()
|
||||||
assert (zp / 'gdpr_export/comments').exists()
|
assert (zp / 'gdpr_export/comments').exists()
|
||||||
# check str constructor just in case
|
# check str constructor just in case
|
||||||
|
@ -77,7 +82,7 @@ def test_zippath() -> None:
|
||||||
|
|
||||||
matched = list(zp.rglob('*'))
|
matched = list(zp.rglob('*'))
|
||||||
assert len(matched) > 0
|
assert len(matched) > 0
|
||||||
assert all(p.filename == str(target) for p in matched), matched
|
assert all(p.filepath == target for p in matched), matched
|
||||||
|
|
||||||
rpaths = [str(p.relative_to(zp)) for p in matched]
|
rpaths = [str(p.relative_to(zp)) for p in matched]
|
||||||
assert rpaths == [
|
assert rpaths == [
|
||||||
|
@ -106,3 +111,7 @@ def test_zippath() -> None:
|
||||||
]
|
]
|
||||||
|
|
||||||
assert list(zp.rglob('mes*')) == [ZipPath(target, 'gdpr_export/messages')]
|
assert list(zp.rglob('mes*')) == [ZipPath(target, 'gdpr_export/messages')]
|
||||||
|
|
||||||
|
iterdir_res = list((zp / 'gdpr_export').iterdir())
|
||||||
|
assert len(iterdir_res) == 3
|
||||||
|
assert all(isinstance(p, Path) for p in iterdir_res)
|
||||||
|
|
Loading…
Add table
Reference in a new issue