GH-113568: Stop raising auditing events from pathlib ABCs (#113571)

Raise auditing events in `pathlib.Path.glob()`, `rglob()` and `walk()`,
but not in `pathlib._abc.PathBase` methods. Also move generation of a
deprecation warning into `pathlib.Path` so it gets the right stack level.
This commit is contained in:
Barney Gale 2024-01-05 21:41:19 +00:00 committed by GitHub
parent 99854ce170
commit 3c4e972d6d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 64 additions and 35 deletions

View file

@ -9,6 +9,8 @@ import io
import ntpath import ntpath
import os import os
import posixpath import posixpath
import sys
import warnings
try: try:
import pwd import pwd
@ -230,7 +232,6 @@ class Path(_abc.PathBase, PurePath):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
if kwargs: if kwargs:
import warnings
msg = ("support for supplying keyword arguments to pathlib.PurePath " msg = ("support for supplying keyword arguments to pathlib.PurePath "
"is deprecated and scheduled for removal in Python {remove}") "is deprecated and scheduled for removal in Python {remove}")
warnings._deprecated("pathlib.PurePath(**kwargs)", msg, remove=(3, 14)) warnings._deprecated("pathlib.PurePath(**kwargs)", msg, remove=(3, 14))
@ -309,6 +310,46 @@ class Path(_abc.PathBase, PurePath):
path._tail_cached = self._tail + [entry.name] path._tail_cached = self._tail + [entry.name]
return path return path
def glob(self, pattern, *, case_sensitive=None, follow_symlinks=None):
"""Iterate over this subtree and yield all existing files (of any
kind, including directories) matching the given relative pattern.
"""
sys.audit("pathlib.Path.glob", self, pattern)
if pattern.endswith('**'):
# 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, 2)
pattern = f'{pattern}/'
return _abc.PathBase.glob(
self, pattern, case_sensitive=case_sensitive, follow_symlinks=follow_symlinks)
def rglob(self, pattern, *, case_sensitive=None, follow_symlinks=None):
"""Recursively yield all existing files (of any kind, including
directories) matching the given relative pattern, anywhere in
this subtree.
"""
sys.audit("pathlib.Path.rglob", self, pattern)
if pattern.endswith('**'):
# 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, 2)
pattern = f'{pattern}/'
pattern = f'**/{pattern}'
return _abc.PathBase.glob(
self, pattern, case_sensitive=case_sensitive, follow_symlinks=follow_symlinks)
def walk(self, top_down=True, on_error=None, follow_symlinks=False):
"""Walk the directory tree from this directory, similar to os.walk()."""
sys.audit("pathlib.Path.walk", self, on_error, follow_symlinks)
return _abc.PathBase.walk(
self, top_down=top_down, on_error=on_error, follow_symlinks=follow_symlinks)
def absolute(self): def absolute(self):
"""Return an absolute version of this path """Return an absolute version of this path
No normalization or symlink resolution is performed. No normalization or symlink resolution is performed.

View file

@ -811,18 +811,6 @@ class PathBase(PurePathBase):
"""Iterate over this subtree and yield all existing files (of any """Iterate over this subtree and yield all existing files (of any
kind, including directories) matching the given relative pattern. kind, including directories) matching the given relative pattern.
""" """
sys.audit("pathlib.Path.glob", self, pattern)
return self._glob(pattern, case_sensitive, follow_symlinks)
def rglob(self, pattern, *, case_sensitive=None, follow_symlinks=None):
"""Recursively yield all existing files (of any kind, including
directories) matching the given relative pattern, anywhere in
this subtree.
"""
sys.audit("pathlib.Path.rglob", self, pattern)
return self._glob(f'**/{pattern}', case_sensitive, follow_symlinks)
def _glob(self, pattern, case_sensitive, follow_symlinks):
path_pattern = self.with_segments(pattern) path_pattern = self.with_segments(pattern)
if path_pattern.drive or path_pattern.root: if path_pattern.drive or path_pattern.root:
raise NotImplementedError("Non-relative patterns are unsupported") raise NotImplementedError("Non-relative patterns are unsupported")
@ -833,14 +821,6 @@ class PathBase(PurePathBase):
if pattern[-1] in (self.pathmod.sep, self.pathmod.altsep): if pattern[-1] in (self.pathmod.sep, self.pathmod.altsep):
# GH-65238: pathlib doesn't preserve trailing slash. Add it back. # GH-65238: pathlib doesn't preserve trailing slash. Add it back.
pattern_parts.append('') 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: if case_sensitive is None:
# TODO: evaluate case-sensitivity of each directory in _select_children(). # TODO: evaluate case-sensitivity of each directory in _select_children().
@ -895,9 +875,16 @@ class PathBase(PurePathBase):
paths = _select_children(paths, dir_only, follow_symlinks, match) paths = _select_children(paths, dir_only, follow_symlinks, match)
return paths return paths
def rglob(self, pattern, *, case_sensitive=None, follow_symlinks=None):
"""Recursively yield all existing files (of any kind, including
directories) matching the given relative pattern, anywhere in
this subtree.
"""
return self.glob(
f'**/{pattern}', case_sensitive=case_sensitive, follow_symlinks=follow_symlinks)
def walk(self, top_down=True, on_error=None, follow_symlinks=False): def walk(self, top_down=True, on_error=None, follow_symlinks=False):
"""Walk the directory tree from this directory, similar to os.walk().""" """Walk the directory tree from this directory, similar to os.walk()."""
sys.audit("pathlib.Path.walk", self, on_error, follow_symlinks)
paths = [self] paths = [self]
while paths: while paths:

View file

@ -1703,6 +1703,18 @@ class PathTest(test_pathlib_abc.DummyPathTest, PurePathTest):
with set_recursion_limit(recursion_limit): 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(self.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('*/**')
@only_posix @only_posix
class PosixPathTest(PathTest, PurePosixPathTest): class PosixPathTest(PathTest, PurePosixPathTest):

View file

@ -1266,19 +1266,6 @@ class DummyPathTest(DummyPurePathTest):
bad_link.symlink_to("bad" * 200) bad_link.symlink_to("bad" * 200)
self.assertEqual(sorted(base.glob('**/*')), [bad_link]) self.assertEqual(sorted(base.glob('**/*')), [bad_link])
def test_glob_recursive_no_trailing_slash(self):
P = self.cls
p = P(self.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 test_readlink(self): def test_readlink(self):
if not self.can_symlink: if not self.can_symlink:
self.skipTest("symlinks required") self.skipTest("symlinks required")

View file

@ -0,0 +1,2 @@
Raise audit events from :class:`pathlib.Path` and not its private base class
``PathBase``.