Skip to content

Commit

Permalink
Merge pull request #85 from Julian/more-pathlib-methods
Browse files Browse the repository at this point in the history
Teach zipp.Path some additional pathlib.Path methods.
  • Loading branch information
jaraco authored Nov 25, 2022
2 parents 8d16d90 + 7d052c1 commit 2c66630
Show file tree
Hide file tree
Showing 3 changed files with 102 additions and 0 deletions.
10 changes: 10 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
v3.11.0
=======

* #85: Added support for new methods on ``Path``:

- ``match``
- ``glob`` and ``rglob``
- ``relative_to``
- ``is_symlink``

v3.10.0
=======

Expand Down
46 changes: 46 additions & 0 deletions tests/test_zipp.py
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
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)

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:
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

0 comments on commit 2c66630

Please sign in to comment.