Skip to content

Commit 6aa35f3

Browse files
authored
gh-122903: Honor directories in zipfile.Path.glob. (#122908)
1 parent 9cd0326 commit 6aa35f3

File tree

4 files changed

+40
-9
lines changed

4 files changed

+40
-9
lines changed

Lib/test/test_zipfile/_path/test_path.py

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ def zipfile_ondisk(self, alpharep):
101101
def test_iterdir_and_types(self, alpharep):
102102
root = zipfile.Path(alpharep)
103103
assert root.is_dir()
104-
a, k, b, g, j = root.iterdir()
104+
a, n, b, g, j = root.iterdir()
105105
assert a.is_file()
106106
assert b.is_dir()
107107
assert g.is_dir()
@@ -121,7 +121,7 @@ def test_is_file_missing(self, alpharep):
121121
@pass_alpharep
122122
def test_iterdir_on_file(self, alpharep):
123123
root = zipfile.Path(alpharep)
124-
a, k, b, g, j = root.iterdir()
124+
a, n, b, g, j = root.iterdir()
125125
with self.assertRaises(ValueError):
126126
a.iterdir()
127127

@@ -136,7 +136,7 @@ def test_subdir_is_dir(self, alpharep):
136136
@pass_alpharep
137137
def test_open(self, alpharep):
138138
root = zipfile.Path(alpharep)
139-
a, k, b, g, j = root.iterdir()
139+
a, n, b, g, j = root.iterdir()
140140
with a.open(encoding="utf-8") as strm:
141141
data = strm.read()
142142
self.assertEqual(data, "content of a")
@@ -240,7 +240,7 @@ def test_open_missing_directory(self, alpharep):
240240
@pass_alpharep
241241
def test_read(self, alpharep):
242242
root = zipfile.Path(alpharep)
243-
a, k, b, g, j = root.iterdir()
243+
a, n, b, g, j = root.iterdir()
244244
assert a.read_text(encoding="utf-8") == "content of a"
245245
# Also check positional encoding arg (gh-101144).
246246
assert a.read_text("utf-8") == "content of a"
@@ -306,7 +306,7 @@ def test_mutability(self, alpharep):
306306
reflect that change.
307307
"""
308308
root = zipfile.Path(alpharep)
309-
a, k, b, g, j = root.iterdir()
309+
a, n, b, g, j = root.iterdir()
310310
alpharep.writestr('foo.txt', 'foo')
311311
alpharep.writestr('bar/baz.txt', 'baz')
312312
assert any(child.name == 'foo.txt' for child in root.iterdir())
@@ -475,6 +475,18 @@ def test_glob_recursive(self, alpharep):
475475

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

478+
@pass_alpharep
479+
def test_glob_dirs(self, alpharep):
480+
root = zipfile.Path(alpharep)
481+
assert list(root.glob('b')) == [zipfile.Path(alpharep, "b/")]
482+
assert list(root.glob('b*')) == [zipfile.Path(alpharep, "b/")]
483+
484+
@pass_alpharep
485+
def test_glob_subdir(self, alpharep):
486+
root = zipfile.Path(alpharep)
487+
assert list(root.glob('g/h')) == [zipfile.Path(alpharep, "g/h/")]
488+
assert list(root.glob('g*/h*')) == [zipfile.Path(alpharep, "g/h/")]
489+
478490
@pass_alpharep
479491
def test_glob_subdirs(self, alpharep):
480492
root = zipfile.Path(alpharep)
@@ -594,3 +606,10 @@ def test_malformed_paths(self):
594606
'two-slash.txt',
595607
'parent.txt',
596608
]
609+
610+
@pass_alpharep
611+
def test_interface(self, alpharep):
612+
from importlib.resources.abc import Traversable
613+
614+
zf = zipfile.Path(alpharep)
615+
assert isinstance(zf, Traversable)

Lib/zipfile/_path/__init__.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,10 @@ def _extract_text_encoding(encoding=None, *args, **kwargs):
250250

251251
class Path:
252252
"""
253-
A pathlib-compatible interface for zip files.
253+
A :class:`importlib.resources.abc.Traversable` interface for zip files.
254+
255+
Implements many of the features users enjoy from
256+
:class:`pathlib.Path`.
254257
255258
Consider a zip file with this structure::
256259
@@ -466,8 +469,7 @@ def glob(self, pattern):
466469
prefix = re.escape(self.at)
467470
tr = Translator(seps='/')
468471
matches = re.compile(prefix + tr.translate(pattern)).fullmatch
469-
names = (data.filename for data in self.root.filelist)
470-
return map(self._next, filter(matches, names))
472+
return map(self._next, filter(matches, self.root.namelist()))
471473

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

Lib/zipfile/_path/glob.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ def translate(self, pattern):
2828
"""
2929
Given a glob pattern, produce a regex that matches it.
3030
"""
31-
return self.extend(self.translate_core(pattern))
31+
return self.extend(self.match_dirs(self.translate_core(pattern)))
3232

3333
def extend(self, pattern):
3434
r"""
@@ -41,6 +41,14 @@ def extend(self, pattern):
4141
"""
4242
return rf'(?s:{pattern})\Z'
4343

44+
def match_dirs(self, pattern):
45+
"""
46+
Ensure that zipfile.Path directory names are matched.
47+
48+
zipfile.Path directory names always end in a slash.
49+
"""
50+
return rf'{pattern}[/]?'
51+
4452
def translate_core(self, pattern):
4553
r"""
4654
Given a glob pattern, produce a regex that matches it.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
``zipfile.Path.glob`` now correctly matches directories instead of
2+
silently omitting them.

0 commit comments

Comments
 (0)