From 7d339216b2a9fa4f8b6b183806cafe75c48ef95b Mon Sep 17 00:00:00 2001 From: barneygale Date: Wed, 28 Apr 2021 22:14:49 +0100 Subject: [PATCH 01/13] Call `accessor.getcwd()` from only one method. --- Lib/pathlib.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Lib/pathlib.py b/Lib/pathlib.py index f1a33178e2958b..11fb7a076160ec 100644 --- a/Lib/pathlib.py +++ b/Lib/pathlib.py @@ -1055,9 +1055,7 @@ def absolute(self): # XXX untested yet! if self.is_absolute(): return self - # FIXME this must defer to the specific flavour (and, under Windows, - # use nt._getfullpathname()) - return self._from_parts([self._accessor.getcwd()] + self._parts) + return self._from_parts([self.cwd()] + self._parts) def resolve(self, strict=False): """ From 740953b27735262d65cc7d29d5c07e2097193672 Mon Sep 17 00:00:00 2001 From: barneygale Date: Wed, 28 Apr 2021 22:15:09 +0100 Subject: [PATCH 02/13] Call `accessor.stat()` from only one method. --- Lib/pathlib.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/pathlib.py b/Lib/pathlib.py index 11fb7a076160ec..09a0fd8edefa68 100644 --- a/Lib/pathlib.py +++ b/Lib/pathlib.py @@ -1005,7 +1005,7 @@ def samefile(self, other_path): try: other_st = other_path.stat() except AttributeError: - other_st = self._accessor.stat(other_path) + other_st = self.__class__(other_path).stat() return os.path.samestat(st, other_st) def iterdir(self): From 57f40f79eab2ada61a6d25929620d69a529a5f6e Mon Sep 17 00:00:00 2001 From: barneygale Date: Wed, 28 Apr 2021 22:15:39 +0100 Subject: [PATCH 03/13] Call `accessor.scandir()` from only one method. --- Lib/pathlib.py | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/Lib/pathlib.py b/Lib/pathlib.py index 09a0fd8edefa68..f838ab192ad2f1 100644 --- a/Lib/pathlib.py +++ b/Lib/pathlib.py @@ -401,15 +401,14 @@ def select_from(self, parent_path): path_cls = type(parent_path) is_dir = path_cls.is_dir exists = path_cls.exists - scandir = parent_path._accessor.scandir if not is_dir(parent_path): return iter([]) - return self._select_from(parent_path, is_dir, exists, scandir) + return self._select_from(parent_path, is_dir, exists) class _TerminatingSelector: - def _select_from(self, parent_path, is_dir, exists, scandir): + def _select_from(self, parent_path, is_dir, exists): yield parent_path @@ -419,11 +418,11 @@ def __init__(self, name, child_parts, flavour): self.name = name _Selector.__init__(self, child_parts, flavour) - def _select_from(self, parent_path, is_dir, exists, scandir): + def _select_from(self, parent_path, is_dir, exists): try: path = parent_path._make_child_relpath(self.name) if (is_dir if self.dironly else exists)(path): - for p in self.successor._select_from(path, is_dir, exists, scandir): + for p in self.successor._select_from(path, is_dir, exists): yield p except PermissionError: return @@ -435,9 +434,9 @@ def __init__(self, pat, child_parts, flavour): self.match = flavour.compile_pattern(pat) _Selector.__init__(self, child_parts, flavour) - def _select_from(self, parent_path, is_dir, exists, scandir): + def _select_from(self, parent_path, is_dir, exists): try: - with scandir(parent_path) as scandir_it: + with parent_path.scandir() as scandir_it: entries = list(scandir_it) for entry in entries: if self.dironly: @@ -454,7 +453,7 @@ def _select_from(self, parent_path, is_dir, exists, scandir): name = entry.name if self.match(name): path = parent_path._make_child_relpath(name) - for p in self.successor._select_from(path, is_dir, exists, scandir): + for p in self.successor._select_from(path, is_dir, exists): yield p except PermissionError: return @@ -465,10 +464,10 @@ class _RecursiveWildcardSelector(_Selector): def __init__(self, pat, child_parts, flavour): _Selector.__init__(self, child_parts, flavour) - def _iterate_directories(self, parent_path, is_dir, scandir): + def _iterate_directories(self, parent_path, is_dir): yield parent_path try: - with scandir(parent_path) as scandir_it: + with parent_path.scandir() as scandir_it: entries = list(scandir_it) for entry in entries: entry_is_dir = False @@ -479,18 +478,18 @@ def _iterate_directories(self, parent_path, is_dir, scandir): raise if entry_is_dir and not entry.is_symlink(): path = parent_path._make_child_relpath(entry.name) - for p in self._iterate_directories(path, is_dir, scandir): + for p in self._iterate_directories(path, is_dir): yield p except PermissionError: return - def _select_from(self, parent_path, is_dir, exists, scandir): + def _select_from(self, parent_path, is_dir, exists): try: yielded = set() try: successor_select = self.successor._select_from - for starting_point in self._iterate_directories(parent_path, is_dir, scandir): - for p in successor_select(starting_point, is_dir, exists, scandir): + for starting_point in self._iterate_directories(parent_path, is_dir): + for p in successor_select(starting_point, is_dir, exists): if p not in yielded: yield p yielded.add(p) @@ -1018,6 +1017,9 @@ def iterdir(self): continue yield self._make_child_relpath(name) + def scandir(self): + return self._accessor.scandir(self) + def glob(self, pattern): """Iterate over this subtree and yield all existing files (of any kind, including directories) matching the given relative pattern. From f3855f80c4ca9a2874a41a0fe9a36a823c047188 Mon Sep 17 00:00:00 2001 From: barneygale Date: Wed, 28 Apr 2021 22:19:45 +0100 Subject: [PATCH 04/13] Call `accessor.link()` from only one method. --- Lib/pathlib.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/pathlib.py b/Lib/pathlib.py index f838ab192ad2f1..c97340f33ff079 100644 --- a/Lib/pathlib.py +++ b/Lib/pathlib.py @@ -1276,7 +1276,7 @@ def link_to(self, target): "for removal in Python 3.12. " "Use pathlib.Path.hardlink_to() instead.", DeprecationWarning, stacklevel=2) - self._accessor.link(self, target) + self.__class__(target).hardlink_to(self) # Convenience functions for querying the stat results From 734940fa9ab49f4c0edd68c1f44db56a9c8ebd29 Mon Sep 17 00:00:00 2001 From: barneygale Date: Wed, 28 Apr 2021 23:12:35 +0100 Subject: [PATCH 05/13] Delete `pathlib._Accessor`. --- Lib/pathlib.py | 161 +++++++++++++-------------------------- Lib/test/test_pathlib.py | 7 +- 2 files changed, 57 insertions(+), 111 deletions(-) diff --git a/Lib/pathlib.py b/Lib/pathlib.py index c97340f33ff079..a25715041d62a0 100644 --- a/Lib/pathlib.py +++ b/Lib/pathlib.py @@ -274,93 +274,6 @@ def make_uri(self, path): _posix_flavour = _PosixFlavour() -class _Accessor: - """An accessor implements a particular (system-specific or not) way of - accessing paths on the filesystem.""" - - -class _NormalAccessor(_Accessor): - - stat = os.stat - - open = io.open - - listdir = os.listdir - - scandir = os.scandir - - chmod = os.chmod - - mkdir = os.mkdir - - unlink = os.unlink - - if hasattr(os, "link"): - link = os.link - else: - def link(self, src, dst): - raise NotImplementedError("os.link() not available on this system") - - rmdir = os.rmdir - - rename = os.rename - - replace = os.replace - - if hasattr(os, "symlink"): - symlink = os.symlink - else: - def symlink(self, src, dst, target_is_directory=False): - raise NotImplementedError("os.symlink() not available on this system") - - def touch(self, path, mode=0o666, exist_ok=True): - if exist_ok: - # First try to bump modification time - # Implementation note: GNU touch uses the UTIME_NOW option of - # the utimensat() / futimens() functions. - try: - os.utime(path, None) - except OSError: - # Avoid exception chaining - pass - else: - return - flags = os.O_CREAT | os.O_WRONLY - if not exist_ok: - flags |= os.O_EXCL - fd = os.open(path, flags, mode) - os.close(fd) - - if hasattr(os, "readlink"): - readlink = os.readlink - else: - def readlink(self, path): - raise NotImplementedError("os.readlink() not available on this system") - - def owner(self, path): - try: - import pwd - return pwd.getpwuid(self.stat(path).st_uid).pw_name - except ImportError: - raise NotImplementedError("Path.owner() is unsupported on this system") - - def group(self, path): - try: - import grp - return grp.getgrgid(self.stat(path).st_gid).gr_name - except ImportError: - raise NotImplementedError("Path.group() is unsupported on this system") - - getcwd = os.getcwd - - expanduser = staticmethod(os.path.expanduser) - - realpath = staticmethod(os.path.realpath) - - -_normal_accessor = _NormalAccessor() - - # # Globbing helpers # @@ -948,7 +861,6 @@ class Path(PurePath): object. You can also instantiate a PosixPath or WindowsPath directly, but cannot instantiate a WindowsPath on a POSIX system or vice versa. """ - _accessor = _normal_accessor __slots__ = () def __new__(cls, *args, **kwargs): @@ -987,7 +899,7 @@ def cwd(cls): """Return a new path pointing to the current working directory (as returned by os.getcwd()). """ - return cls(cls._accessor.getcwd()) + return cls(os.getcwd()) @classmethod def home(cls): @@ -1011,14 +923,14 @@ def iterdir(self): """Iterate over the files in this directory. Does not yield any result for the special paths '.' and '..'. """ - for name in self._accessor.listdir(self): + for name in os.listdir(self): if name in {'.', '..'}: # Yielding a path object for these makes little sense continue yield self._make_child_relpath(name) def scandir(self): - return self._accessor.scandir(self) + return os.scandir(self) def glob(self, pattern): """Iterate over this subtree and yield all existing files (of any @@ -1072,7 +984,7 @@ def check_eloop(e): raise RuntimeError("Symlink loop from %r" % e.filename) try: - s = self._accessor.realpath(self, strict=strict) + s = os.path.realpath(self, strict=strict) except OSError as e: check_eloop(e) raise @@ -1092,19 +1004,28 @@ def stat(self, *, follow_symlinks=True): Return the result of the stat() system call on this path, like os.stat() does. """ - return self._accessor.stat(self, follow_symlinks=follow_symlinks) + return os.stat(self, follow_symlinks=follow_symlinks) def owner(self): """ Return the login name of the file owner. """ - return self._accessor.owner(self) + try: + import pwd + return pwd.getpwuid(self.stat().st_uid).pw_name + except ImportError: + raise NotImplementedError("Path.owner() is unsupported on this system") def group(self): """ Return the group name of the file gid. """ - return self._accessor.group(self) + + try: + import grp + return grp.getgrgid(self.stat().st_gid).gr_name + except ImportError: + raise NotImplementedError("Path.group() is unsupported on this system") def open(self, mode='r', buffering=-1, encoding=None, errors=None, newline=None): @@ -1114,8 +1035,7 @@ def open(self, mode='r', buffering=-1, encoding=None, """ if "b" not in mode: encoding = io.text_encoding(encoding) - return self._accessor.open(self, mode, buffering, encoding, errors, - newline) + return io.open(self, mode, buffering, encoding, errors, newline) def read_bytes(self): """ @@ -1156,21 +1076,40 @@ def readlink(self): """ Return the path to which the symbolic link points. """ - path = self._accessor.readlink(self) + if hasattr(os, "readlink"): + path = os.readlink(self) + else: + raise NotImplementedError("os.readlink() not available on this system") return self._from_parts((path,)) def touch(self, mode=0o666, exist_ok=True): """ Create this file with the given access mode, if it doesn't exist. """ - self._accessor.touch(self, mode, exist_ok) + + if exist_ok: + # First try to bump modification time + # Implementation note: GNU touch uses the UTIME_NOW option of + # the utimensat() / futimens() functions. + try: + os.utime(self, None) + except OSError: + # Avoid exception chaining + pass + else: + return + flags = os.O_CREAT | os.O_WRONLY + if not exist_ok: + flags |= os.O_EXCL + fd = os.open(self, flags, mode) + os.close(fd) def mkdir(self, mode=0o777, parents=False, exist_ok=False): """ Create a new directory at this given path. """ try: - self._accessor.mkdir(self, mode) + os.mkdir(self, mode) except FileNotFoundError: if not parents or self.parent == self: raise @@ -1186,7 +1125,7 @@ def chmod(self, mode, *, follow_symlinks=True): """ Change the permissions of the path, like os.chmod(). """ - self._accessor.chmod(self, mode, follow_symlinks=follow_symlinks) + os.chmod(self, mode, follow_symlinks=follow_symlinks) def lchmod(self, mode): """ @@ -1201,7 +1140,7 @@ def unlink(self, missing_ok=False): If the path is a directory, use rmdir() instead. """ try: - self._accessor.unlink(self) + os.unlink(self) except FileNotFoundError: if not missing_ok: raise @@ -1210,7 +1149,7 @@ def rmdir(self): """ Remove this directory. The directory must be empty. """ - self._accessor.rmdir(self) + os.rmdir(self) def lstat(self): """ @@ -1229,7 +1168,7 @@ def rename(self, target): Returns the new Path instance pointing to the target path. """ - self._accessor.rename(self, target) + os.rename(self, target) return self.__class__(target) def replace(self, target): @@ -1242,7 +1181,7 @@ def replace(self, target): Returns the new Path instance pointing to the target path. """ - self._accessor.replace(self, target) + os.replace(self, target) return self.__class__(target) def symlink_to(self, target, target_is_directory=False): @@ -1250,7 +1189,10 @@ def symlink_to(self, target, target_is_directory=False): Make this path a symlink pointing to the target path. Note the order of arguments (link, target) is the reverse of os.symlink. """ - self._accessor.symlink(target, self, target_is_directory) + if hasattr(os, "symlink"): + os.symlink(target, self, target_is_directory) + else: + raise NotImplementedError("os.symlink() not available on this system") def hardlink_to(self, target): """ @@ -1258,7 +1200,10 @@ def hardlink_to(self, target): Note the order of arguments (self, target) is the reverse of os.link's. """ - self._accessor.link(target, self) + if hasattr(os, "link"): + os.link(target, self) + else: + raise NotImplementedError("os.link() not available on this system") def link_to(self, target): """ @@ -1433,7 +1378,7 @@ def expanduser(self): """ if (not (self._drv or self._root) and self._parts and self._parts[0][:1] == '~'): - homedir = self._accessor.expanduser(self._parts[0]) + homedir = os.path.expanduser(self._parts[0]) if homedir[:1] == "~": raise RuntimeError("Could not determine home directory.") return self._from_parts([homedir] + self._parts[1:]) diff --git a/Lib/test/test_pathlib.py b/Lib/test/test_pathlib.py index 555c7ee795bd12..a8ff8f37fcff9e 100644 --- a/Lib/test/test_pathlib.py +++ b/Lib/test/test_pathlib.py @@ -2177,6 +2177,7 @@ def test_mkdir_concurrent_parent_creation(self): p = self.cls(BASE, 'dirCPC%d' % pattern_num) self.assertFalse(p.exists()) + real_mkdir = os.mkdir def my_mkdir(path, mode=0o777): path = str(path) # Emulate another process that would create the directory @@ -2185,15 +2186,15 @@ def my_mkdir(path, mode=0o777): # function is called at most 5 times (dirCPC/dir1/dir2, # dirCPC/dir1, dirCPC, dirCPC/dir1, dirCPC/dir1/dir2). if pattern.pop(): - os.mkdir(path, mode) # From another process. + real_mkdir(path, mode) # From another process. concurrently_created.add(path) - os.mkdir(path, mode) # Our real call. + real_mkdir(path, mode) # Our real call. pattern = [bool(pattern_num & (1 << n)) for n in range(5)] concurrently_created = set() p12 = p / 'dir1' / 'dir2' try: - with mock.patch("pathlib._normal_accessor.mkdir", my_mkdir): + with mock.patch("os.mkdir", my_mkdir): p12.mkdir(parents=True, exist_ok=False) except FileExistsError: self.assertIn(str(p12), concurrently_created) From 3dfefdfee037205809c69e208cc51bf023cc4a92 Mon Sep 17 00:00:00 2001 From: barneygale Date: Wed, 28 Apr 2021 23:33:41 +0100 Subject: [PATCH 06/13] Fix broken `test_glob_permissions` test. --- Lib/test/test_pathlib.py | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/Lib/test/test_pathlib.py b/Lib/test/test_pathlib.py index a8ff8f37fcff9e..f3ba3800b31bcc 100644 --- a/Lib/test/test_pathlib.py +++ b/Lib/test/test_pathlib.py @@ -1,3 +1,4 @@ +import contextlib import collections.abc import io import os @@ -1716,21 +1717,18 @@ def test_glob_permissions(self): # Patching is needed to avoid relying on the filesystem # to return the order of the files as the error will not # happen if the symlink is the last item. - - with mock.patch("os.scandir") as scandir: - scandir.return_value = sorted(os.scandir(base)) + real_scandir = os.scandir + def my_scandir(path): + with real_scandir(path) as scandir_it: + entries = list(scandir_it) + entries.sort(key=lambda entry: entry.name) + return contextlib.nullcontext(entries) + + with mock.patch("os.scandir", my_scandir): self.assertEqual(len(set(base.glob("*"))), 3) - - subdir.mkdir() - - with mock.patch("os.scandir") as scandir: - scandir.return_value = sorted(os.scandir(base)) + subdir.mkdir() self.assertEqual(len(set(base.glob("*"))), 4) - - subdir.chmod(000) - - with mock.patch("os.scandir") as scandir: - scandir.return_value = sorted(os.scandir(base)) + subdir.chmod(000) self.assertEqual(len(set(base.glob("*"))), 4) def _check_resolve(self, p, expected, strict=True): From 613a1718b474b8f7401edebdb8fa40af940d7299 Mon Sep 17 00:00:00 2001 From: barneygale Date: Thu, 13 May 2021 23:05:02 +0100 Subject: [PATCH 07/13] Rename `Path.scandir()` to `Path._scandir()` --- Lib/pathlib.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Lib/pathlib.py b/Lib/pathlib.py index a25715041d62a0..3b4870df7fd1b5 100644 --- a/Lib/pathlib.py +++ b/Lib/pathlib.py @@ -349,7 +349,7 @@ def __init__(self, pat, child_parts, flavour): def _select_from(self, parent_path, is_dir, exists): try: - with parent_path.scandir() as scandir_it: + with parent_path._scandir() as scandir_it: entries = list(scandir_it) for entry in entries: if self.dironly: @@ -380,7 +380,7 @@ def __init__(self, pat, child_parts, flavour): def _iterate_directories(self, parent_path, is_dir): yield parent_path try: - with parent_path.scandir() as scandir_it: + with parent_path._scandir() as scandir_it: entries = list(scandir_it) for entry in entries: entry_is_dir = False @@ -929,7 +929,7 @@ def iterdir(self): continue yield self._make_child_relpath(name) - def scandir(self): + def _scandir(self): return os.scandir(self) def glob(self, pattern): From d07f465bd94326a759e11ac8e1505eee273bf595 Mon Sep 17 00:00:00 2001 From: barneygale Date: Fri, 14 May 2021 11:52:50 +0100 Subject: [PATCH 08/13] Simplify handling of missing `os.readlink()`, `symlink()` and `link()`. --- Lib/pathlib.py | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/Lib/pathlib.py b/Lib/pathlib.py index 3b4870df7fd1b5..d3cb9e16cfce8d 100644 --- a/Lib/pathlib.py +++ b/Lib/pathlib.py @@ -1076,11 +1076,9 @@ def readlink(self): """ Return the path to which the symbolic link points. """ - if hasattr(os, "readlink"): - path = os.readlink(self) - else: + if not hasattr(os, "readlink"): raise NotImplementedError("os.readlink() not available on this system") - return self._from_parts((path,)) + return self._from_parts((os.readlink(self),)) def touch(self, mode=0o666, exist_ok=True): """ @@ -1189,10 +1187,9 @@ def symlink_to(self, target, target_is_directory=False): Make this path a symlink pointing to the target path. Note the order of arguments (link, target) is the reverse of os.symlink. """ - if hasattr(os, "symlink"): - os.symlink(target, self, target_is_directory) - else: + if not hasattr(os, "symlink"): raise NotImplementedError("os.symlink() not available on this system") + os.symlink(target, self, target_is_directory) def hardlink_to(self, target): """ @@ -1200,10 +1197,9 @@ def hardlink_to(self, target): Note the order of arguments (self, target) is the reverse of os.link's. """ - if hasattr(os, "link"): - os.link(target, self) - else: + if not hasattr(os, "link"): raise NotImplementedError("os.link() not available on this system") + os.link(target, self) def link_to(self, target): """ From 013ddc6b4c2e2cb079736d28b5849c58122f1722 Mon Sep 17 00:00:00 2001 From: barneygale Date: Wed, 5 Jan 2022 02:22:53 +0000 Subject: [PATCH 09/13] Add comment explaining why we access the filesystem via `Path` objects in glob selectors --- Lib/pathlib.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Lib/pathlib.py b/Lib/pathlib.py index d3cb9e16cfce8d..d8c96ee7aa8f72 100644 --- a/Lib/pathlib.py +++ b/Lib/pathlib.py @@ -349,6 +349,10 @@ def __init__(self, pat, child_parts, flavour): def _select_from(self, parent_path, is_dir, exists): try: + # bpo-24132: a future version of pathlib will support subclassing + # of pathlib.Path to customize how the filesystem is accessed. We + # must honour those customizations here, rather than calling + # os.scandir() directly. with parent_path._scandir() as scandir_it: entries = list(scandir_it) for entry in entries: From c9ffcf90d855e2aaf389ee889c72d797c41a629e Mon Sep 17 00:00:00 2001 From: barneygale Date: Wed, 5 Jan 2022 02:58:23 +0000 Subject: [PATCH 10/13] Move comment to `Path._scandir()` definition. --- Lib/pathlib.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/Lib/pathlib.py b/Lib/pathlib.py index d8c96ee7aa8f72..0964058ed24929 100644 --- a/Lib/pathlib.py +++ b/Lib/pathlib.py @@ -349,10 +349,6 @@ def __init__(self, pat, child_parts, flavour): def _select_from(self, parent_path, is_dir, exists): try: - # bpo-24132: a future version of pathlib will support subclassing - # of pathlib.Path to customize how the filesystem is accessed. We - # must honour those customizations here, rather than calling - # os.scandir() directly. with parent_path._scandir() as scandir_it: entries = list(scandir_it) for entry in entries: @@ -934,6 +930,9 @@ def iterdir(self): yield self._make_child_relpath(name) def _scandir(self): + # bpo-24132: a future version of pathlib will support subclassing of + # pathlib.Path to customize how the filesystem is accessed. This + # includes scandir(), which is used to implement glob(). return os.scandir(self) def glob(self, pattern): From 3783a6ae97f651d0a9eb56fb46bab2eaf208059b Mon Sep 17 00:00:00 2001 From: barneygale Date: Wed, 5 Jan 2022 03:09:55 +0000 Subject: [PATCH 11/13] Add news entry --- .../next/Library/2022-01-05-03-09-29.bpo-43012.RVhLIL.rst | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2022-01-05-03-09-29.bpo-43012.RVhLIL.rst diff --git a/Misc/NEWS.d/next/Library/2022-01-05-03-09-29.bpo-43012.RVhLIL.rst b/Misc/NEWS.d/next/Library/2022-01-05-03-09-29.bpo-43012.RVhLIL.rst new file mode 100644 index 00000000000000..c6a6723a96b34a --- /dev/null +++ b/Misc/NEWS.d/next/Library/2022-01-05-03-09-29.bpo-43012.RVhLIL.rst @@ -0,0 +1,2 @@ +The pathlib module's obsolete and internal ``_Accessor`` class has been +removed to prepare the terrain for upcoming enhancements to the module. From 8aabc5d4a9d6b6681c75e60ec2a59e74358da5b9 Mon Sep 17 00:00:00 2001 From: barneygale Date: Sun, 9 Jan 2022 02:08:22 +0000 Subject: [PATCH 12/13] Undo changes to selector classes. These aren't necessary, and may slow things down. --- Lib/pathlib.py | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/Lib/pathlib.py b/Lib/pathlib.py index 0964058ed24929..a8fa9e50971c1d 100644 --- a/Lib/pathlib.py +++ b/Lib/pathlib.py @@ -314,14 +314,15 @@ def select_from(self, parent_path): path_cls = type(parent_path) is_dir = path_cls.is_dir exists = path_cls.exists + scandir = path_cls._scandir if not is_dir(parent_path): return iter([]) - return self._select_from(parent_path, is_dir, exists) + return self._select_from(parent_path, is_dir, exists, scandir) class _TerminatingSelector: - def _select_from(self, parent_path, is_dir, exists): + def _select_from(self, parent_path, is_dir, exists, scandir): yield parent_path @@ -331,11 +332,11 @@ def __init__(self, name, child_parts, flavour): self.name = name _Selector.__init__(self, child_parts, flavour) - def _select_from(self, parent_path, is_dir, exists): + def _select_from(self, parent_path, is_dir, exists, scandir): try: path = parent_path._make_child_relpath(self.name) if (is_dir if self.dironly else exists)(path): - for p in self.successor._select_from(path, is_dir, exists): + for p in self.successor._select_from(path, is_dir, exists, scandir): yield p except PermissionError: return @@ -347,9 +348,9 @@ def __init__(self, pat, child_parts, flavour): self.match = flavour.compile_pattern(pat) _Selector.__init__(self, child_parts, flavour) - def _select_from(self, parent_path, is_dir, exists): + def _select_from(self, parent_path, is_dir, exists, scandir): try: - with parent_path._scandir() as scandir_it: + with scandir(parent_path) as scandir_it: entries = list(scandir_it) for entry in entries: if self.dironly: @@ -366,7 +367,7 @@ def _select_from(self, parent_path, is_dir, exists): name = entry.name if self.match(name): path = parent_path._make_child_relpath(name) - for p in self.successor._select_from(path, is_dir, exists): + for p in self.successor._select_from(path, is_dir, exists, scandir): yield p except PermissionError: return @@ -377,10 +378,10 @@ class _RecursiveWildcardSelector(_Selector): def __init__(self, pat, child_parts, flavour): _Selector.__init__(self, child_parts, flavour) - def _iterate_directories(self, parent_path, is_dir): + def _iterate_directories(self, parent_path, is_dir, scandir): yield parent_path try: - with parent_path._scandir() as scandir_it: + with scandir(parent_path) as scandir_it: entries = list(scandir_it) for entry in entries: entry_is_dir = False @@ -391,18 +392,18 @@ def _iterate_directories(self, parent_path, is_dir): raise if entry_is_dir and not entry.is_symlink(): path = parent_path._make_child_relpath(entry.name) - for p in self._iterate_directories(path, is_dir): + for p in self._iterate_directories(path, is_dir, scandir): yield p except PermissionError: return - def _select_from(self, parent_path, is_dir, exists): + def _select_from(self, parent_path, is_dir, exists, scandir): try: yielded = set() try: successor_select = self.successor._select_from - for starting_point in self._iterate_directories(parent_path, is_dir): - for p in successor_select(starting_point, is_dir, exists): + for starting_point in self._iterate_directories(parent_path, is_dir, scandir): + for p in successor_select(starting_point, is_dir, exists, scandir): if p not in yielded: yield p yielded.add(p) From cfaf15fdd5bfff653b27ab8caf999c82119668e4 Mon Sep 17 00:00:00 2001 From: barneygale Date: Sat, 29 Jan 2022 01:01:16 +0000 Subject: [PATCH 13/13] Fix up broken merge from main. --- Lib/test/test_pathlib.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_pathlib.py b/Lib/test/test_pathlib.py index 1f4bb5c80c926c..5e46b4ffeae100 100644 --- a/Lib/test/test_pathlib.py +++ b/Lib/test/test_pathlib.py @@ -1460,7 +1460,7 @@ def test_cwd(self): def test_absolute_common(self): P = self.cls - with mock.patch("pathlib._normal_accessor.getcwd") as getcwd: + with mock.patch("os.getcwd") as getcwd: getcwd.return_value = BASE # Simple relative paths. @@ -2675,7 +2675,7 @@ def test_absolute(self): self.assertEqual(str(P(share + 'a\\b').absolute()), share + 'a\\b') # UNC relative paths. - with mock.patch("pathlib._normal_accessor.getcwd") as getcwd: + with mock.patch("os.getcwd") as getcwd: getcwd.return_value = share self.assertEqual(str(P().absolute()), share)