Skip to content

Commit ea2f5bc

Browse files
bpo-22276: Change pathlib.Path.glob not to ignore trailing path separator (GH-10349)
Now pathlib.Path.glob() **only** matches directories when the pattern ends in a path separator. Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
1 parent 0ef8d92 commit ea2f5bc

File tree

5 files changed

+42
-0
lines changed

5 files changed

+42
-0
lines changed

Doc/library/pathlib.rst

+6
Original file line numberDiff line numberDiff line change
@@ -815,6 +815,9 @@ call fails (for example because the path doesn't exist).
815815

816816
.. audit-event:: pathlib.Path.glob self,pattern pathlib.Path.glob
817817

818+
.. versionchanged:: 3.11
819+
Return only directories if *pattern* ends with a pathname components
820+
separator (:data:`~os.sep` or :data:`~os.altsep`).
818821

819822
.. method:: Path.group()
820823

@@ -1104,6 +1107,9 @@ call fails (for example because the path doesn't exist).
11041107

11051108
.. audit-event:: pathlib.Path.rglob self,pattern pathlib.Path.rglob
11061109

1110+
.. versionchanged:: 3.11
1111+
Return only directories if *pattern* ends with a pathname components
1112+
separator (:data:`~os.sep` or :data:`~os.altsep`).
11071113

11081114
.. method:: Path.rmdir()
11091115

Doc/whatsnew/3.11.rst

+9
Original file line numberDiff line numberDiff line change
@@ -506,6 +506,15 @@ os
506506
instead of ``CryptGenRandom()`` which is deprecated.
507507
(Contributed by Dong-hee Na in :issue:`44611`.)
508508

509+
510+
pathlib
511+
-------
512+
513+
* :meth:`~pathlib.Path.glob` and :meth:`~pathlib.Path.rglob` return only
514+
directories if *pattern* ends with a pathname components separator:
515+
:data:`~os.sep` or :data:`~os.altsep`.
516+
(Contributed by Eisuke Kawasima in :issue:`22276` and :issue:`33392`.)
517+
509518
re
510519
--
511520

Lib/pathlib.py

+6
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,8 @@ def make_uri(self, path):
281281
def _make_selector(pattern_parts, flavour):
282282
pat = pattern_parts[0]
283283
child_parts = pattern_parts[1:]
284+
if not pat:
285+
return _TerminatingSelector()
284286
if pat == '**':
285287
cls = _RecursiveWildcardSelector
286288
elif '**' in pat:
@@ -943,6 +945,8 @@ def glob(self, pattern):
943945
drv, root, pattern_parts = self._flavour.parse_parts((pattern,))
944946
if drv or root:
945947
raise NotImplementedError("Non-relative patterns are unsupported")
948+
if pattern[-1] in (self._flavour.sep, self._flavour.altsep):
949+
pattern_parts.append('')
946950
selector = _make_selector(tuple(pattern_parts), self._flavour)
947951
for p in selector.select_from(self):
948952
yield p
@@ -956,6 +960,8 @@ def rglob(self, pattern):
956960
drv, root, pattern_parts = self._flavour.parse_parts((pattern,))
957961
if drv or root:
958962
raise NotImplementedError("Non-relative patterns are unsupported")
963+
if pattern[-1] in (self._flavour.sep, self._flavour.altsep):
964+
pattern_parts.append('')
959965
selector = _make_selector(("**",) + tuple(pattern_parts), self._flavour)
960966
for p in selector.select_from(self):
961967
yield p

Lib/test/test_pathlib.py

+17
Original file line numberDiff line numberDiff line change
@@ -1662,6 +1662,11 @@ def _check(glob, expected):
16621662
else:
16631663
_check(p.glob("*/fileB"), ['dirB/fileB', 'linkB/fileB'])
16641664

1665+
if not os_helper.can_symlink():
1666+
_check(p.glob("*/"), ["dirA", "dirB", "dirC", "dirE"])
1667+
else:
1668+
_check(p.glob("*/"), ["dirA", "dirB", "dirC", "dirE", "linkB"])
1669+
16651670
def test_rglob_common(self):
16661671
def _check(glob, expected):
16671672
self.assertEqual(set(glob), { P(BASE, q) for q in expected })
@@ -1679,6 +1684,16 @@ def _check(glob, expected):
16791684
"linkB/fileB", "dirA/linkC/fileB"])
16801685
_check(p.rglob("file*"), ["fileA", "dirB/fileB",
16811686
"dirC/fileC", "dirC/dirD/fileD"])
1687+
if not os_helper.can_symlink():
1688+
_check(p.rglob("*/"), [
1689+
"dirA", "dirB", "dirC", "dirC/dirD", "dirE",
1690+
])
1691+
else:
1692+
_check(p.rglob("*/"), [
1693+
"dirA", "dirA/linkC", "dirB", "dirB/linkD", "dirC",
1694+
"dirC/dirD", "dirE", "linkB",
1695+
])
1696+
16821697
p = P(BASE, "dirC")
16831698
_check(p.rglob("file*"), ["dirC/fileC", "dirC/dirD/fileD"])
16841699
_check(p.rglob("*/*"), ["dirC/dirD/fileD"])
@@ -2704,6 +2719,7 @@ def test_glob(self):
27042719
P = self.cls
27052720
p = P(BASE)
27062721
self.assertEqual(set(p.glob("FILEa")), { P(BASE, "fileA") })
2722+
self.assertEqual(set(p.glob("*a\\")), { P(BASE, "dirA") })
27072723
self.assertEqual(set(p.glob("F*a")), { P(BASE, "fileA") })
27082724
self.assertEqual(set(map(str, p.glob("FILEa"))), {f"{p}\\FILEa"})
27092725
self.assertEqual(set(map(str, p.glob("F*a"))), {f"{p}\\fileA"})
@@ -2712,6 +2728,7 @@ def test_rglob(self):
27122728
P = self.cls
27132729
p = P(BASE, "dirC")
27142730
self.assertEqual(set(p.rglob("FILEd")), { P(BASE, "dirC/dirD/fileD") })
2731+
self.assertEqual(set(p.rglob("*\\")), { P(BASE, "dirC/dirD") })
27152732
self.assertEqual(set(map(str, p.rglob("FILEd"))), {f"{p}\\dirD\\FILEd"})
27162733

27172734
def test_expanduser(self):
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
:class:`~pathlib.Path` methods :meth:`~pathlib.Path.glob` and :meth:`~pathlib.Path.rglob` return only
2+
directories if *pattern* ends with a pathname components separator
3+
(``/`` or :data:`~os.sep`).
4+
Patch by Eisuke Kawashima.

0 commit comments

Comments
 (0)