summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Lib/test/test_zipfile/_path/test_path.py29
-rw-r--r--Lib/zipfile/_path/__init__.py8
-rw-r--r--Lib/zipfile/_path/glob.py10
-rw-r--r--Misc/NEWS.d/next/Library/2024-08-11-14-23-07.gh-issue-122903.xktZta.rst2
4 files changed, 40 insertions, 9 deletions
diff --git a/Lib/test/test_zipfile/_path/test_path.py b/Lib/test/test_zipfile/_path/test_path.py
index 4a31cae..90d6df0 100644
--- a/Lib/test/test_zipfile/_path/test_path.py
+++ b/Lib/test/test_zipfile/_path/test_path.py
@@ -101,7 +101,7 @@ class TestPath(unittest.TestCase):
def test_iterdir_and_types(self, alpharep):
root = zipfile.Path(alpharep)
assert root.is_dir()
- a, k, b, g, j = root.iterdir()
+ a, n, b, g, j = root.iterdir()
assert a.is_file()
assert b.is_dir()
assert g.is_dir()
@@ -121,7 +121,7 @@ class TestPath(unittest.TestCase):
@pass_alpharep
def test_iterdir_on_file(self, alpharep):
root = zipfile.Path(alpharep)
- a, k, b, g, j = root.iterdir()
+ a, n, b, g, j = root.iterdir()
with self.assertRaises(ValueError):
a.iterdir()
@@ -136,7 +136,7 @@ class TestPath(unittest.TestCase):
@pass_alpharep
def test_open(self, alpharep):
root = zipfile.Path(alpharep)
- a, k, b, g, j = root.iterdir()
+ a, n, b, g, j = root.iterdir()
with a.open(encoding="utf-8") as strm:
data = strm.read()
self.assertEqual(data, "content of a")
@@ -240,7 +240,7 @@ class TestPath(unittest.TestCase):
@pass_alpharep
def test_read(self, alpharep):
root = zipfile.Path(alpharep)
- a, k, b, g, j = root.iterdir()
+ a, n, b, g, j = root.iterdir()
assert a.read_text(encoding="utf-8") == "content of a"
# Also check positional encoding arg (gh-101144).
assert a.read_text("utf-8") == "content of a"
@@ -306,7 +306,7 @@ class TestPath(unittest.TestCase):
reflect that change.
"""
root = zipfile.Path(alpharep)
- a, k, b, g, j = root.iterdir()
+ a, n, b, g, j = root.iterdir()
alpharep.writestr('foo.txt', 'foo')
alpharep.writestr('bar/baz.txt', 'baz')
assert any(child.name == 'foo.txt' for child in root.iterdir())
@@ -476,6 +476,18 @@ class TestPath(unittest.TestCase):
assert list(root.glob("**/*.txt")) == list(root.rglob("*.txt"))
@pass_alpharep
+ def test_glob_dirs(self, alpharep):
+ root = zipfile.Path(alpharep)
+ assert list(root.glob('b')) == [zipfile.Path(alpharep, "b/")]
+ assert list(root.glob('b*')) == [zipfile.Path(alpharep, "b/")]
+
+ @pass_alpharep
+ def test_glob_subdir(self, alpharep):
+ root = zipfile.Path(alpharep)
+ assert list(root.glob('g/h')) == [zipfile.Path(alpharep, "g/h/")]
+ assert list(root.glob('g*/h*')) == [zipfile.Path(alpharep, "g/h/")]
+
+ @pass_alpharep
def test_glob_subdirs(self, alpharep):
root = zipfile.Path(alpharep)
@@ -594,3 +606,10 @@ class TestPath(unittest.TestCase):
'two-slash.txt',
'parent.txt',
]
+
+ @pass_alpharep
+ def test_interface(self, alpharep):
+ from importlib.resources.abc import Traversable
+
+ zf = zipfile.Path(alpharep)
+ assert isinstance(zf, Traversable)
diff --git a/Lib/zipfile/_path/__init__.py b/Lib/zipfile/_path/__init__.py
index 0f18147..3c01659 100644
--- a/Lib/zipfile/_path/__init__.py
+++ b/Lib/zipfile/_path/__init__.py
@@ -250,7 +250,10 @@ def _extract_text_encoding(encoding=None, *args, **kwargs):
class Path:
"""
- A pathlib-compatible interface for zip files.
+ A :class:`importlib.resources.abc.Traversable` interface for zip files.
+
+ Implements many of the features users enjoy from
+ :class:`pathlib.Path`.
Consider a zip file with this structure::
@@ -466,8 +469,7 @@ class Path:
prefix = re.escape(self.at)
tr = Translator(seps='/')
matches = re.compile(prefix + tr.translate(pattern)).fullmatch
- names = (data.filename for data in self.root.filelist)
- return map(self._next, filter(matches, names))
+ return map(self._next, filter(matches, self.root.namelist()))
def rglob(self, pattern):
return self.glob(f'**/{pattern}')
diff --git a/Lib/zipfile/_path/glob.py b/Lib/zipfile/_path/glob.py
index 69c41d7..4320f1c 100644
--- a/Lib/zipfile/_path/glob.py
+++ b/Lib/zipfile/_path/glob.py
@@ -28,7 +28,7 @@ class Translator:
"""
Given a glob pattern, produce a regex that matches it.
"""
- return self.extend(self.translate_core(pattern))
+ return self.extend(self.match_dirs(self.translate_core(pattern)))
def extend(self, pattern):
r"""
@@ -41,6 +41,14 @@ class Translator:
"""
return rf'(?s:{pattern})\Z'
+ def match_dirs(self, pattern):
+ """
+ Ensure that zipfile.Path directory names are matched.
+
+ zipfile.Path directory names always end in a slash.
+ """
+ return rf'{pattern}[/]?'
+
def translate_core(self, pattern):
r"""
Given a glob pattern, produce a regex that matches it.
diff --git a/Misc/NEWS.d/next/Library/2024-08-11-14-23-07.gh-issue-122903.xktZta.rst b/Misc/NEWS.d/next/Library/2024-08-11-14-23-07.gh-issue-122903.xktZta.rst
new file mode 100644
index 0000000..c2a1e64
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2024-08-11-14-23-07.gh-issue-122903.xktZta.rst
@@ -0,0 +1,2 @@
+``zipfile.Path.glob`` now correctly matches directories instead of
+silently omitting them.