From 937b12a6ac6c1d8b84e65d9b8a46aa6a881bde28 Mon Sep 17 00:00:00 2001 From: Eisuke Kawashima Date: Sun, 11 Nov 2018 05:40:15 +0900 Subject: [PATCH 1/2] bpo-22276: Fix pathlib.Path.glob not to ignore trailing path separator --- Doc/library/pathlib.rst | 6 ++++++ Doc/whatsnew/3.11.rst | 9 +++++++++ Lib/pathlib.py | 6 ++++++ Lib/test/test_pathlib.py | 17 +++++++++++++++++ .../2018-11-11-04-41-11.bpo-22276.Tt19TW.rst | 4 ++++ 5 files changed, 42 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2018-11-11-04-41-11.bpo-22276.Tt19TW.rst diff --git a/Doc/library/pathlib.rst b/Doc/library/pathlib.rst index 1e7bc315471e2c..b9e207c7fd7e13 100644 --- a/Doc/library/pathlib.rst +++ b/Doc/library/pathlib.rst @@ -815,6 +815,9 @@ call fails (for example because the path doesn't exist). .. audit-event:: pathlib.Path.glob self,pattern pathlib.Path.glob + .. versionchanged:: 3.11 + Return only directories if *pattern* ends with a pathname components + separator (:data:`~os.sep` or :data:`~os.altsep`). .. method:: Path.group() @@ -1104,6 +1107,9 @@ call fails (for example because the path doesn't exist). .. audit-event:: pathlib.Path.rglob self,pattern pathlib.Path.rglob + .. versionchanged:: 3.11 + Return only directories if *pattern* ends with a pathname components + separator (:data:`~os.sep` or :data:`~os.altsep`). .. method:: Path.rmdir() diff --git a/Doc/whatsnew/3.11.rst b/Doc/whatsnew/3.11.rst index aa0a51b4375f18..716a77148dc2ae 100644 --- a/Doc/whatsnew/3.11.rst +++ b/Doc/whatsnew/3.11.rst @@ -506,6 +506,15 @@ os instead of ``CryptGenRandom()`` which is deprecated. (Contributed by Dong-hee Na in :issue:`44611`.) + +pathlib +------- + +* :meth:`~pathlib.Path.glob` and :meth:`~pathlib.Path.rglob` return only + directories if *pattern* ends with a pathname components separator: + :data:`~os.sep` or :data:`~os.altsep`. + (Contributed by Eisuke Kawasima in :issue:`22276` and :issue:`33392`.) + re -- diff --git a/Lib/pathlib.py b/Lib/pathlib.py index 4763ab54f6ba81..1f098fe6bd5f37 100644 --- a/Lib/pathlib.py +++ b/Lib/pathlib.py @@ -281,6 +281,8 @@ def make_uri(self, path): def _make_selector(pattern_parts, flavour): pat = pattern_parts[0] child_parts = pattern_parts[1:] + if not pat: + return _TerminatingSelector() if pat == '**': cls = _RecursiveWildcardSelector elif '**' in pat: @@ -943,6 +945,8 @@ def glob(self, pattern): drv, root, pattern_parts = self._flavour.parse_parts((pattern,)) if drv or root: raise NotImplementedError("Non-relative patterns are unsupported") + if pattern[-1] in (self._flavour.sep, self._flavour.altsep): + pattern_parts.append('') selector = _make_selector(tuple(pattern_parts), self._flavour) for p in selector.select_from(self): yield p @@ -956,6 +960,8 @@ def rglob(self, pattern): drv, root, pattern_parts = self._flavour.parse_parts((pattern,)) if drv or root: raise NotImplementedError("Non-relative patterns are unsupported") + if pattern[-1] in (self._flavour.sep, self._flavour.altsep): + pattern_parts.append('') selector = _make_selector(("**",) + tuple(pattern_parts), self._flavour) for p in selector.select_from(self): yield p diff --git a/Lib/test/test_pathlib.py b/Lib/test/test_pathlib.py index b8b08bf0ce1bb5..6737068c0ff6d6 100644 --- a/Lib/test/test_pathlib.py +++ b/Lib/test/test_pathlib.py @@ -1662,6 +1662,11 @@ def _check(glob, expected): else: _check(p.glob("*/fileB"), ['dirB/fileB', 'linkB/fileB']) + if not os_helper.can_symlink(): + _check(p.glob("*/"), ["dirA", "dirB", "dirC", "dirE"]) + else: + _check(p.glob("*/"), ["dirA", "dirB", "dirC", "dirE", "linkB"]) + def test_rglob_common(self): def _check(glob, expected): self.assertEqual(set(glob), { P(BASE, q) for q in expected }) @@ -1679,6 +1684,16 @@ def _check(glob, expected): "linkB/fileB", "dirA/linkC/fileB"]) _check(p.rglob("file*"), ["fileA", "dirB/fileB", "dirC/fileC", "dirC/dirD/fileD"]) + if not os_helper.can_symlink(): + _check(p.rglob("*/"), [ + "dirA", "dirB", "dirC", "dirC/dirD", "dirE", + ]) + else: + _check(p.rglob("*/"), [ + "dirA", "dirA/linkC", "dirB", "dirB/linkD", "dirC", + "dirC/dirD", "dirE", "linkB", + ]) + p = P(BASE, "dirC") _check(p.rglob("file*"), ["dirC/fileC", "dirC/dirD/fileD"]) _check(p.rglob("*/*"), ["dirC/dirD/fileD"]) @@ -2704,6 +2719,7 @@ def test_glob(self): P = self.cls p = P(BASE) self.assertEqual(set(p.glob("FILEa")), { P(BASE, "fileA") }) + self.assertEqual(set(p.glob("*a\\")), { P(BASE, "dirA") }) self.assertEqual(set(p.glob("F*a")), { P(BASE, "fileA") }) self.assertEqual(set(map(str, p.glob("FILEa"))), {f"{p}\\FILEa"}) self.assertEqual(set(map(str, p.glob("F*a"))), {f"{p}\\fileA"}) @@ -2712,6 +2728,7 @@ def test_rglob(self): P = self.cls p = P(BASE, "dirC") self.assertEqual(set(p.rglob("FILEd")), { P(BASE, "dirC/dirD/fileD") }) + self.assertEqual(set(p.rglob("*\\")), { P(BASE, "dirC/dirD") }) self.assertEqual(set(map(str, p.rglob("FILEd"))), {f"{p}\\dirD\\FILEd"}) def test_expanduser(self): diff --git a/Misc/NEWS.d/next/Library/2018-11-11-04-41-11.bpo-22276.Tt19TW.rst b/Misc/NEWS.d/next/Library/2018-11-11-04-41-11.bpo-22276.Tt19TW.rst new file mode 100644 index 00000000000000..ae9da594104d74 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2018-11-11-04-41-11.bpo-22276.Tt19TW.rst @@ -0,0 +1,4 @@ +:meth:`~pathlib.Path.glob` and :meth:`~pathlib.Path.rglob` return only +directories if *pattern* ends with a pathname components separator +(``/`` or :data:`~os.sep`). +Patch by Eisuke Kawashima. From 8ecc162553c5d0e1d14628319278353056346d04 Mon Sep 17 00:00:00 2001 From: Eisuke Kawashima Date: Wed, 27 Apr 2022 23:51:36 +0900 Subject: [PATCH 2/2] Update Misc/NEWS.d/next/Library/2018-11-11-04-41-11.bpo-22276.Tt19TW.rst Co-authored-by: Serhiy Storchaka --- .../next/Library/2018-11-11-04-41-11.bpo-22276.Tt19TW.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Library/2018-11-11-04-41-11.bpo-22276.Tt19TW.rst b/Misc/NEWS.d/next/Library/2018-11-11-04-41-11.bpo-22276.Tt19TW.rst index ae9da594104d74..357c3f4ae0e6a8 100644 --- a/Misc/NEWS.d/next/Library/2018-11-11-04-41-11.bpo-22276.Tt19TW.rst +++ b/Misc/NEWS.d/next/Library/2018-11-11-04-41-11.bpo-22276.Tt19TW.rst @@ -1,4 +1,4 @@ -:meth:`~pathlib.Path.glob` and :meth:`~pathlib.Path.rglob` return only +:class:`~pathlib.Path` methods :meth:`~pathlib.Path.glob` and :meth:`~pathlib.Path.rglob` return only directories if *pattern* ends with a pathname components separator (``/`` or :data:`~os.sep`). Patch by Eisuke Kawashima.