Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Teach zipp.Path some additional pathlib.Path methods. #85

Merged
merged 11 commits into from Nov 25, 2022
46 changes: 46 additions & 0 deletions tests/test_zipp.py
Expand Up @@ -403,6 +403,52 @@ def test_root_unnamed(self, alpharep):
assert sub.name == "b"
assert sub.parent

@pass_alpharep
def test_match_and_glob(self, alpharep):
root = zipp.Path(alpharep)
assert not root.match("*.txt")

assert list(root.glob("b/c.*")) == [zipp.Path(alpharep, "b/c.txt")]

files = root.glob("**/*.txt")
assert all(each.match("*.txt") for each in files)

assert list(root.glob("**/*.txt")) == list(root.rglob("*.txt"))

def test_glob_empty(self):
root = zipp.Path(zipfile.ZipFile(io.BytesIO(), 'w'))
with self.assertRaises(ValueError):
root.glob('')

@pass_alpharep
def test_eq_hash(self, alpharep):
root = zipp.Path(alpharep)
assert root == zipp.Path(alpharep)

assert root != (root / "a.txt")
assert (root / "a.txt") == (root / "a.txt")

root = zipp.Path(alpharep)
assert root in {root}

@pass_alpharep
def test_is_symlink(self, alpharep):
"""
See python/cpython#82102 for symlink support beyond this object.
"""

root = zipp.Path(alpharep)
assert not root.is_symlink()

@pass_alpharep
def test_relative_to(self, alpharep):
root = zipp.Path(alpharep)
relative = root.joinpath("b", "c.txt").relative_to(root / "b")
assert str(relative) == "c.txt"

relative = root.joinpath("b", "d", "e.txt").relative_to(root / "b")
assert str(relative) == "d/e.txt"

@pass_alpharep
def test_inheritance(self, alpharep):
cls = type('PathChild', (zipp.Path,), {})
Expand Down
46 changes: 46 additions & 0 deletions zipp/__init__.py
Expand Up @@ -4,6 +4,8 @@
import itertools
import contextlib
import pathlib
import re
import fnmatch

from .py310compat import text_encoding

Expand Down Expand Up @@ -243,6 +245,18 @@ def __init__(self, root, at=""):
self.root = FastLookup.make(root)
self.at = at

def __eq__(self, other):
"""
>>> Path(zipfile.ZipFile(io.BytesIO(), 'w')) == 'foo'
False
"""
if self.__class__ is not other.__class__:
return NotImplemented
jaraco marked this conversation as resolved.
Show resolved Hide resolved
return (self.root, self.at) == (other.root, other.at)

def __hash__(self):
return hash((self.root, self.at))

def open(self, mode='r', *args, pwd=None, **kwargs):
"""
Open this entry as text or binary following the semantics
Expand Down Expand Up @@ -313,6 +327,38 @@ def iterdir(self):
subs = map(self._next, self.root.namelist())
return filter(self._is_child, subs)

def match(self, path_pattern):
return pathlib.Path(self.at).match(path_pattern)
jaraco marked this conversation as resolved.
Show resolved Hide resolved

def is_symlink(self):
"""
Return whether this path is a symlink. Always false (python/cpython#82102).
"""
return False

def _descendants(self):
for child in self.iterdir():
yield child
if child.is_dir():
yield from child._descendants()

def glob(self, pattern):
if not pattern:
jaraco marked this conversation as resolved.
Show resolved Hide resolved
raise ValueError("Unacceptable pattern: {!r}".format(pattern))

matches = re.compile(fnmatch.translate(pattern)).fullmatch
return (
child
for child in self._descendants()
if matches(str(child.relative_to(self)))
)

def rglob(self, pattern):
return self.glob(f'**/{pattern}')

def relative_to(self, other, *extra):
return posixpath.relpath(str(self), str(other.joinpath(*extra)))

def __str__(self):
return posixpath.join(self.root.filename, self.at)

Expand Down