Skip to content

Commit 2c66630

Browse files
authored
Merge pull request #85 from Julian/more-pathlib-methods
Teach zipp.Path some additional pathlib.Path methods.
2 parents 8d16d90 + 7d052c1 commit 2c66630

File tree

3 files changed

+102
-0
lines changed

3 files changed

+102
-0
lines changed

CHANGES.rst

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,13 @@
1+
v3.11.0
2+
=======
3+
4+
* #85: Added support for new methods on ``Path``:
5+
6+
- ``match``
7+
- ``glob`` and ``rglob``
8+
- ``relative_to``
9+
- ``is_symlink``
10+
111
v3.10.0
212
=======
313

tests/test_zipp.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -403,6 +403,52 @@ def test_root_unnamed(self, alpharep):
403403
assert sub.name == "b"
404404
assert sub.parent
405405

406+
@pass_alpharep
407+
def test_match_and_glob(self, alpharep):
408+
root = zipp.Path(alpharep)
409+
assert not root.match("*.txt")
410+
411+
assert list(root.glob("b/c.*")) == [zipp.Path(alpharep, "b/c.txt")]
412+
413+
files = root.glob("**/*.txt")
414+
assert all(each.match("*.txt") for each in files)
415+
416+
assert list(root.glob("**/*.txt")) == list(root.rglob("*.txt"))
417+
418+
def test_glob_empty(self):
419+
root = zipp.Path(zipfile.ZipFile(io.BytesIO(), 'w'))
420+
with self.assertRaises(ValueError):
421+
root.glob('')
422+
423+
@pass_alpharep
424+
def test_eq_hash(self, alpharep):
425+
root = zipp.Path(alpharep)
426+
assert root == zipp.Path(alpharep)
427+
428+
assert root != (root / "a.txt")
429+
assert (root / "a.txt") == (root / "a.txt")
430+
431+
root = zipp.Path(alpharep)
432+
assert root in {root}
433+
434+
@pass_alpharep
435+
def test_is_symlink(self, alpharep):
436+
"""
437+
See python/cpython#82102 for symlink support beyond this object.
438+
"""
439+
440+
root = zipp.Path(alpharep)
441+
assert not root.is_symlink()
442+
443+
@pass_alpharep
444+
def test_relative_to(self, alpharep):
445+
root = zipp.Path(alpharep)
446+
relative = root.joinpath("b", "c.txt").relative_to(root / "b")
447+
assert str(relative) == "c.txt"
448+
449+
relative = root.joinpath("b", "d", "e.txt").relative_to(root / "b")
450+
assert str(relative) == "d/e.txt"
451+
406452
@pass_alpharep
407453
def test_inheritance(self, alpharep):
408454
cls = type('PathChild', (zipp.Path,), {})

zipp/__init__.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
import itertools
55
import contextlib
66
import pathlib
7+
import re
8+
import fnmatch
79

810
from .py310compat import text_encoding
911

@@ -243,6 +245,18 @@ def __init__(self, root, at=""):
243245
self.root = FastLookup.make(root)
244246
self.at = at
245247

248+
def __eq__(self, other):
249+
"""
250+
>>> Path(zipfile.ZipFile(io.BytesIO(), 'w')) == 'foo'
251+
False
252+
"""
253+
if self.__class__ is not other.__class__:
254+
return NotImplemented
255+
return (self.root, self.at) == (other.root, other.at)
256+
257+
def __hash__(self):
258+
return hash((self.root, self.at))
259+
246260
def open(self, mode='r', *args, pwd=None, **kwargs):
247261
"""
248262
Open this entry as text or binary following the semantics
@@ -313,6 +327,38 @@ def iterdir(self):
313327
subs = map(self._next, self.root.namelist())
314328
return filter(self._is_child, subs)
315329

330+
def match(self, path_pattern):
331+
return pathlib.Path(self.at).match(path_pattern)
332+
333+
def is_symlink(self):
334+
"""
335+
Return whether this path is a symlink. Always false (python/cpython#82102).
336+
"""
337+
return False
338+
339+
def _descendants(self):
340+
for child in self.iterdir():
341+
yield child
342+
if child.is_dir():
343+
yield from child._descendants()
344+
345+
def glob(self, pattern):
346+
if not pattern:
347+
raise ValueError("Unacceptable pattern: {!r}".format(pattern))
348+
349+
matches = re.compile(fnmatch.translate(pattern)).fullmatch
350+
return (
351+
child
352+
for child in self._descendants()
353+
if matches(str(child.relative_to(self)))
354+
)
355+
356+
def rglob(self, pattern):
357+
return self.glob(f'**/{pattern}')
358+
359+
def relative_to(self, other, *extra):
360+
return posixpath.relpath(str(self), str(other.joinpath(*extra)))
361+
316362
def __str__(self):
317363
return posixpath.join(self.root.filename, self.at)
318364

0 commit comments

Comments
 (0)