From f63bca9894fbdba1717989bb8f7dd07279f2d528 Mon Sep 17 00:00:00 2001 From: barneygale Date: Wed, 7 Jun 2023 00:15:56 +0100 Subject: [PATCH 1/6] GH-70303: Emit FutureWarning when pathlib glob pattern ends with `**`. In a future Python release, patterns with this ending will match both files and directories. Users may add a trailing slash to remove the warning. --- Doc/library/pathlib.rst | 5 +++++ Lib/pathlib.py | 5 +++++ Lib/test/test_pathlib.py | 19 ++++++++++++++++--- ...3-06-07-00-13-00.gh-issue-70303.frwUKH.rst | 4 ++++ 4 files changed, 30 insertions(+), 3 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-06-07-00-13-00.gh-issue-70303.frwUKH.rst diff --git a/Doc/library/pathlib.rst b/Doc/library/pathlib.rst index ad801d5d7cdc4b..04a0999d4c50aa 100644 --- a/Doc/library/pathlib.rst +++ b/Doc/library/pathlib.rst @@ -947,6 +947,11 @@ call fails (for example because the path doesn't exist). .. versionadded:: 3.13 The *follow_symlinks* argument. + .. versionchanged:: 3.13 + Emits :exc:`FutureWarning` if the pattern ends with "``**``". In a + future Python release, patterns with this ending will match both files + and directories. Add a trailing slash to match only directories. + .. method:: Path.group() Return the name of the group owning the file. :exc:`KeyError` is raised diff --git a/Lib/pathlib.py b/Lib/pathlib.py index 89c7b1ebdf727f..de92a91843c6ae 100644 --- a/Lib/pathlib.py +++ b/Lib/pathlib.py @@ -1045,6 +1045,11 @@ def _glob(self, pattern, case_sensitive, follow_symlinks): pattern_parts.append('') if pattern_parts[-1] == '**': # GH-70303: '**' only matches directories. Add trailing slash. + warnings.warn( + "Pattern ending '**' will match files and directories in a " + "future Python release. Add a trailing slash to match only " + "directories and remove this warning.", + FutureWarning, 3) pattern_parts.append('') if case_sensitive is None: diff --git a/Lib/test/test_pathlib.py b/Lib/test/test_pathlib.py index 1a008e5cea3f00..6fa16ee79bf636 100644 --- a/Lib/test/test_pathlib.py +++ b/Lib/test/test_pathlib.py @@ -1965,11 +1965,11 @@ def _check(glob, expected): "dirC/dirD", "dirC/dirD/fileD"]) _check(p.rglob("file*"), ["dirC/fileC", "dirC/dirD/fileD"]) _check(p.rglob("**/file*"), ["dirC/fileC", "dirC/dirD/fileD"]) - _check(p.rglob("dir*/**"), ["dirC/dirD"]) + _check(p.rglob("dir*/**/"), ["dirC/dirD"]) _check(p.rglob("*/*"), ["dirC/dirD/fileD"]) _check(p.rglob("*/"), ["dirC/dirD"]) _check(p.rglob(""), ["dirC", "dirC/dirD"]) - _check(p.rglob("**"), ["dirC", "dirC/dirD"]) + _check(p.rglob("**/"), ["dirC", "dirC/dirD"]) # gh-91616, a re module regression _check(p.rglob("*.txt"), ["dirC/novel.txt"]) _check(p.rglob("*.*"), ["dirC/novel.txt"]) @@ -2115,7 +2115,20 @@ def test_glob_above_recursion_limit(self): path.mkdir(parents=True) with set_recursion_limit(recursion_limit): - list(base.glob('**')) + list(base.glob('**/')) + + def test_glob_recursive_no_trailing_slash(self): + P = self.cls + p = P(BASE) + with self.assertWarns(FutureWarning): + p.glob('**') + with self.assertWarns(FutureWarning): + p.glob('*/**') + with self.assertWarns(FutureWarning): + p.rglob('**') + with self.assertWarns(FutureWarning): + p.rglob('*/**') + def _check_resolve(self, p, expected, strict=True): q = p.resolve(strict) diff --git a/Misc/NEWS.d/next/Library/2023-06-07-00-13-00.gh-issue-70303.frwUKH.rst b/Misc/NEWS.d/next/Library/2023-06-07-00-13-00.gh-issue-70303.frwUKH.rst new file mode 100644 index 00000000000000..16cc6a7bb18b43 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-06-07-00-13-00.gh-issue-70303.frwUKH.rst @@ -0,0 +1,4 @@ +Emit :exc:`FutureWarning` from :meth:`pathlib.Path.glob` and +:meth:`~pathlib.Path.rglob` if the given pattern ends with "``**``". In a +future Python release, patterns with this ending will match both files and +directories. Add a trailing slash to match only directories. From d1a17165ed6e227a39b409354cb5e6752e1d1b2b Mon Sep 17 00:00:00 2001 From: barneygale Date: Sun, 18 Jun 2023 03:09:33 +0100 Subject: [PATCH 2/6] Fix handling of patterns ending `**` --- Doc/library/pathlib.rst | 10 +++++----- Lib/pathlib.py | 8 -------- Lib/test/test_pathlib.py | 18 ++++-------------- ...23-06-07-00-13-00.gh-issue-70303.frwUKH.rst | 7 +++---- 4 files changed, 12 insertions(+), 31 deletions(-) diff --git a/Doc/library/pathlib.rst b/Doc/library/pathlib.rst index 04a0999d4c50aa..84869c20fdb549 100644 --- a/Doc/library/pathlib.rst +++ b/Doc/library/pathlib.rst @@ -906,8 +906,8 @@ call fails (for example because the path doesn't exist). >>> sorted(Path('.').glob('*/*.py')) [PosixPath('docs/conf.py')] - Patterns are the same as for :mod:`fnmatch`, with the addition of "``**``" - which means "this directory and all subdirectories, recursively". In other + Pattern segments are the same as for :mod:`fnmatch`, with the addition of + "``**``" which matches all files, directories and subdirectories. In other words, it enables recursive globbing:: >>> sorted(Path('.').glob('**/*.py')) @@ -948,9 +948,9 @@ call fails (for example because the path doesn't exist). The *follow_symlinks* argument. .. versionchanged:: 3.13 - Emits :exc:`FutureWarning` if the pattern ends with "``**``". In a - future Python release, patterns with this ending will match both files - and directories. Add a trailing slash to match only directories. + Patterns ending with "``**``" match files and directories. In previous + versions, patterns with this ending match only directories. + .. method:: Path.group() diff --git a/Lib/pathlib.py b/Lib/pathlib.py index de92a91843c6ae..510e5e4d72f5c4 100644 --- a/Lib/pathlib.py +++ b/Lib/pathlib.py @@ -1043,14 +1043,6 @@ def _glob(self, pattern, case_sensitive, follow_symlinks): if pattern[-1] in (self._flavour.sep, self._flavour.altsep): # GH-65238: pathlib doesn't preserve trailing slash. Add it back. pattern_parts.append('') - if pattern_parts[-1] == '**': - # GH-70303: '**' only matches directories. Add trailing slash. - warnings.warn( - "Pattern ending '**' will match files and directories in a " - "future Python release. Add a trailing slash to match only " - "directories and remove this warning.", - FutureWarning, 3) - pattern_parts.append('') if case_sensitive is None: # TODO: evaluate case-sensitivity of each directory in _select_children(). diff --git a/Lib/test/test_pathlib.py b/Lib/test/test_pathlib.py index 6fa16ee79bf636..7eef4183c25aad 100644 --- a/Lib/test/test_pathlib.py +++ b/Lib/test/test_pathlib.py @@ -1965,10 +1965,13 @@ def _check(glob, expected): "dirC/dirD", "dirC/dirD/fileD"]) _check(p.rglob("file*"), ["dirC/fileC", "dirC/dirD/fileD"]) _check(p.rglob("**/file*"), ["dirC/fileC", "dirC/dirD/fileD"]) + _check(p.rglob("dir*/**"), ["dirC/dirD", "dirC/dirD/fileD"]) _check(p.rglob("dir*/**/"), ["dirC/dirD"]) _check(p.rglob("*/*"), ["dirC/dirD/fileD"]) _check(p.rglob("*/"), ["dirC/dirD"]) _check(p.rglob(""), ["dirC", "dirC/dirD"]) + _check(p.rglob("**"), ["dirC", "dirC/fileC", "dirC/novel.txt", + "dirC/dirD", "dirC/dirD/fileD"]) _check(p.rglob("**/"), ["dirC", "dirC/dirD"]) # gh-91616, a re module regression _check(p.rglob("*.txt"), ["dirC/novel.txt"]) @@ -2115,20 +2118,7 @@ def test_glob_above_recursion_limit(self): path.mkdir(parents=True) with set_recursion_limit(recursion_limit): - list(base.glob('**/')) - - def test_glob_recursive_no_trailing_slash(self): - P = self.cls - p = P(BASE) - with self.assertWarns(FutureWarning): - p.glob('**') - with self.assertWarns(FutureWarning): - p.glob('*/**') - with self.assertWarns(FutureWarning): - p.rglob('**') - with self.assertWarns(FutureWarning): - p.rglob('*/**') - + list(base.glob('**')) def _check_resolve(self, p, expected, strict=True): q = p.resolve(strict) diff --git a/Misc/NEWS.d/next/Library/2023-06-07-00-13-00.gh-issue-70303.frwUKH.rst b/Misc/NEWS.d/next/Library/2023-06-07-00-13-00.gh-issue-70303.frwUKH.rst index 16cc6a7bb18b43..fb472ef4d682d6 100644 --- a/Misc/NEWS.d/next/Library/2023-06-07-00-13-00.gh-issue-70303.frwUKH.rst +++ b/Misc/NEWS.d/next/Library/2023-06-07-00-13-00.gh-issue-70303.frwUKH.rst @@ -1,4 +1,3 @@ -Emit :exc:`FutureWarning` from :meth:`pathlib.Path.glob` and -:meth:`~pathlib.Path.rglob` if the given pattern ends with "``**``". In a -future Python release, patterns with this ending will match both files and -directories. Add a trailing slash to match only directories. +:meth:`pathlib.Path.glob` and :meth:`~pathlib.Path.rglob` now yield files and +directories when expanding a pattern that ends with "``**``". In a earlier +Python releases, patterns with this ending match only directories. From 22be3a62b5c1ff3d478e908eb465ac5b61a4acde Mon Sep 17 00:00:00 2001 From: barneygale Date: Sun, 18 Jun 2023 18:25:09 +0100 Subject: [PATCH 3/6] Add whatsnew entry; fix news typo. --- Doc/whatsnew/3.13.rst | 5 +++++ .../Library/2023-06-07-00-13-00.gh-issue-70303.frwUKH.rst | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/Doc/whatsnew/3.13.rst b/Doc/whatsnew/3.13.rst index 502cafdaf5e19f..941003ae1ec53e 100644 --- a/Doc/whatsnew/3.13.rst +++ b/Doc/whatsnew/3.13.rst @@ -106,6 +106,11 @@ built on debug mode `. pathlib ------- +* :meth:`pathlib.Path.glob` and :meth:`~pathlib.Path.rglob` now match files + and directories when expanding a pattern that ends with "``**``". In + earlier Python releases, patterns with this ending match only directories. + (Contributed by Barney Gale in :gh:`70303`.) + * Add support for recursive wildcards in :meth:`pathlib.PurePath.match`. (Contributed by Barney Gale in :gh:`73435`.) diff --git a/Misc/NEWS.d/next/Library/2023-06-07-00-13-00.gh-issue-70303.frwUKH.rst b/Misc/NEWS.d/next/Library/2023-06-07-00-13-00.gh-issue-70303.frwUKH.rst index fb472ef4d682d6..b544e5ec214c5a 100644 --- a/Misc/NEWS.d/next/Library/2023-06-07-00-13-00.gh-issue-70303.frwUKH.rst +++ b/Misc/NEWS.d/next/Library/2023-06-07-00-13-00.gh-issue-70303.frwUKH.rst @@ -1,3 +1,3 @@ :meth:`pathlib.Path.glob` and :meth:`~pathlib.Path.rglob` now yield files and -directories when expanding a pattern that ends with "``**``". In a earlier +directories when expanding a pattern that ends with "``**``". In earlier Python releases, patterns with this ending match only directories. From 91bbce12597c61a4c59d4b4062a0f301e6c431f7 Mon Sep 17 00:00:00 2001 From: barneygale Date: Tue, 20 Jun 2023 23:36:38 +0100 Subject: [PATCH 4/6] Revert "Add whatsnew entry; fix news typo." This reverts commit 22be3a62b5c1ff3d478e908eb465ac5b61a4acde. --- Doc/whatsnew/3.13.rst | 5 ----- .../Library/2023-06-07-00-13-00.gh-issue-70303.frwUKH.rst | 2 +- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/Doc/whatsnew/3.13.rst b/Doc/whatsnew/3.13.rst index 941003ae1ec53e..502cafdaf5e19f 100644 --- a/Doc/whatsnew/3.13.rst +++ b/Doc/whatsnew/3.13.rst @@ -106,11 +106,6 @@ built on debug mode `. pathlib ------- -* :meth:`pathlib.Path.glob` and :meth:`~pathlib.Path.rglob` now match files - and directories when expanding a pattern that ends with "``**``". In - earlier Python releases, patterns with this ending match only directories. - (Contributed by Barney Gale in :gh:`70303`.) - * Add support for recursive wildcards in :meth:`pathlib.PurePath.match`. (Contributed by Barney Gale in :gh:`73435`.) diff --git a/Misc/NEWS.d/next/Library/2023-06-07-00-13-00.gh-issue-70303.frwUKH.rst b/Misc/NEWS.d/next/Library/2023-06-07-00-13-00.gh-issue-70303.frwUKH.rst index b544e5ec214c5a..fb472ef4d682d6 100644 --- a/Misc/NEWS.d/next/Library/2023-06-07-00-13-00.gh-issue-70303.frwUKH.rst +++ b/Misc/NEWS.d/next/Library/2023-06-07-00-13-00.gh-issue-70303.frwUKH.rst @@ -1,3 +1,3 @@ :meth:`pathlib.Path.glob` and :meth:`~pathlib.Path.rglob` now yield files and -directories when expanding a pattern that ends with "``**``". In earlier +directories when expanding a pattern that ends with "``**``". In a earlier Python releases, patterns with this ending match only directories. From 05661371a77978514a6301ee491e6ed4a61426f1 Mon Sep 17 00:00:00 2001 From: barneygale Date: Tue, 20 Jun 2023 23:36:46 +0100 Subject: [PATCH 5/6] Revert "Fix handling of patterns ending `**`" This reverts commit d1a17165ed6e227a39b409354cb5e6752e1d1b2b. --- Doc/library/pathlib.rst | 10 +++++----- Lib/pathlib.py | 8 ++++++++ Lib/test/test_pathlib.py | 18 ++++++++++++++---- ...23-06-07-00-13-00.gh-issue-70303.frwUKH.rst | 7 ++++--- 4 files changed, 31 insertions(+), 12 deletions(-) diff --git a/Doc/library/pathlib.rst b/Doc/library/pathlib.rst index 84869c20fdb549..04a0999d4c50aa 100644 --- a/Doc/library/pathlib.rst +++ b/Doc/library/pathlib.rst @@ -906,8 +906,8 @@ call fails (for example because the path doesn't exist). >>> sorted(Path('.').glob('*/*.py')) [PosixPath('docs/conf.py')] - Pattern segments are the same as for :mod:`fnmatch`, with the addition of - "``**``" which matches all files, directories and subdirectories. In other + Patterns are the same as for :mod:`fnmatch`, with the addition of "``**``" + which means "this directory and all subdirectories, recursively". In other words, it enables recursive globbing:: >>> sorted(Path('.').glob('**/*.py')) @@ -948,9 +948,9 @@ call fails (for example because the path doesn't exist). The *follow_symlinks* argument. .. versionchanged:: 3.13 - Patterns ending with "``**``" match files and directories. In previous - versions, patterns with this ending match only directories. - + Emits :exc:`FutureWarning` if the pattern ends with "``**``". In a + future Python release, patterns with this ending will match both files + and directories. Add a trailing slash to match only directories. .. method:: Path.group() diff --git a/Lib/pathlib.py b/Lib/pathlib.py index 510e5e4d72f5c4..de92a91843c6ae 100644 --- a/Lib/pathlib.py +++ b/Lib/pathlib.py @@ -1043,6 +1043,14 @@ def _glob(self, pattern, case_sensitive, follow_symlinks): if pattern[-1] in (self._flavour.sep, self._flavour.altsep): # GH-65238: pathlib doesn't preserve trailing slash. Add it back. pattern_parts.append('') + if pattern_parts[-1] == '**': + # GH-70303: '**' only matches directories. Add trailing slash. + warnings.warn( + "Pattern ending '**' will match files and directories in a " + "future Python release. Add a trailing slash to match only " + "directories and remove this warning.", + FutureWarning, 3) + pattern_parts.append('') if case_sensitive is None: # TODO: evaluate case-sensitivity of each directory in _select_children(). diff --git a/Lib/test/test_pathlib.py b/Lib/test/test_pathlib.py index 7eef4183c25aad..6fa16ee79bf636 100644 --- a/Lib/test/test_pathlib.py +++ b/Lib/test/test_pathlib.py @@ -1965,13 +1965,10 @@ def _check(glob, expected): "dirC/dirD", "dirC/dirD/fileD"]) _check(p.rglob("file*"), ["dirC/fileC", "dirC/dirD/fileD"]) _check(p.rglob("**/file*"), ["dirC/fileC", "dirC/dirD/fileD"]) - _check(p.rglob("dir*/**"), ["dirC/dirD", "dirC/dirD/fileD"]) _check(p.rglob("dir*/**/"), ["dirC/dirD"]) _check(p.rglob("*/*"), ["dirC/dirD/fileD"]) _check(p.rglob("*/"), ["dirC/dirD"]) _check(p.rglob(""), ["dirC", "dirC/dirD"]) - _check(p.rglob("**"), ["dirC", "dirC/fileC", "dirC/novel.txt", - "dirC/dirD", "dirC/dirD/fileD"]) _check(p.rglob("**/"), ["dirC", "dirC/dirD"]) # gh-91616, a re module regression _check(p.rglob("*.txt"), ["dirC/novel.txt"]) @@ -2118,7 +2115,20 @@ def test_glob_above_recursion_limit(self): path.mkdir(parents=True) with set_recursion_limit(recursion_limit): - list(base.glob('**')) + list(base.glob('**/')) + + def test_glob_recursive_no_trailing_slash(self): + P = self.cls + p = P(BASE) + with self.assertWarns(FutureWarning): + p.glob('**') + with self.assertWarns(FutureWarning): + p.glob('*/**') + with self.assertWarns(FutureWarning): + p.rglob('**') + with self.assertWarns(FutureWarning): + p.rglob('*/**') + def _check_resolve(self, p, expected, strict=True): q = p.resolve(strict) diff --git a/Misc/NEWS.d/next/Library/2023-06-07-00-13-00.gh-issue-70303.frwUKH.rst b/Misc/NEWS.d/next/Library/2023-06-07-00-13-00.gh-issue-70303.frwUKH.rst index fb472ef4d682d6..16cc6a7bb18b43 100644 --- a/Misc/NEWS.d/next/Library/2023-06-07-00-13-00.gh-issue-70303.frwUKH.rst +++ b/Misc/NEWS.d/next/Library/2023-06-07-00-13-00.gh-issue-70303.frwUKH.rst @@ -1,3 +1,4 @@ -:meth:`pathlib.Path.glob` and :meth:`~pathlib.Path.rglob` now yield files and -directories when expanding a pattern that ends with "``**``". In a earlier -Python releases, patterns with this ending match only directories. +Emit :exc:`FutureWarning` from :meth:`pathlib.Path.glob` and +:meth:`~pathlib.Path.rglob` if the given pattern ends with "``**``". In a +future Python release, patterns with this ending will match both files and +directories. Add a trailing slash to match only directories. From a7f47aafe4df1812d1597d404f10be4b4bb0a8b4 Mon Sep 17 00:00:00 2001 From: barneygale Date: Fri, 4 Aug 2023 23:44:16 +0100 Subject: [PATCH 6/6] Do-nothing commit to try to wake GitHub up :/ --- .../next/Library/2023-06-07-00-13-00.gh-issue-70303.frwUKH.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Library/2023-06-07-00-13-00.gh-issue-70303.frwUKH.rst b/Misc/NEWS.d/next/Library/2023-06-07-00-13-00.gh-issue-70303.frwUKH.rst index 16cc6a7bb18b43..39a891ac5964ab 100644 --- a/Misc/NEWS.d/next/Library/2023-06-07-00-13-00.gh-issue-70303.frwUKH.rst +++ b/Misc/NEWS.d/next/Library/2023-06-07-00-13-00.gh-issue-70303.frwUKH.rst @@ -1,4 +1,4 @@ Emit :exc:`FutureWarning` from :meth:`pathlib.Path.glob` and :meth:`~pathlib.Path.rglob` if the given pattern ends with "``**``". In a future Python release, patterns with this ending will match both files and -directories. Add a trailing slash to match only directories. +directories. Add a trailing slash to only match directories.