Skip to content

Commit 08f8301

Browse files
authored
bpo-43012: remove pathlib._Accessor (GH-25701)
Per Pitrou: > The original intent for the “accessor” thing was to have a variant that did all accesses under a filesystem tree in a race condition-free way using openat and friends. It turned out to be much too hairy to actually implement, so was entirely abandoned, but the accessor abstraction was left there. https://discuss.python.org/t/make-pathlib-extensible/3428/2 Accessors are: - Lacking any internal purpose - '_NormalAccessor' is the only implementation - Lacking any firm conceptual difference to `Path` objects themselves (inc. subclasses) - Non-public, i.e. underscore prefixed - '_Accessor' and '_NormalAccessor' - Unofficially used to implement customized `Path` objects, but once once [bpo-24132]() is addressed there will be a supported route for that. This patch preserves all existing behaviour.
1 parent 187930f commit 08f8301

File tree

3 files changed

+78
-130
lines changed

3 files changed

+78
-130
lines changed

Lib/pathlib.py

Lines changed: 59 additions & 112 deletions
Original file line numberDiff line numberDiff line change
@@ -275,93 +275,6 @@ def make_uri(self, path):
275275
_posix_flavour = _PosixFlavour()
276276

277277

278-
class _Accessor:
279-
"""An accessor implements a particular (system-specific or not) way of
280-
accessing paths on the filesystem."""
281-
282-
283-
class _NormalAccessor(_Accessor):
284-
285-
stat = os.stat
286-
287-
open = io.open
288-
289-
listdir = os.listdir
290-
291-
scandir = os.scandir
292-
293-
chmod = os.chmod
294-
295-
mkdir = os.mkdir
296-
297-
unlink = os.unlink
298-
299-
if hasattr(os, "link"):
300-
link = os.link
301-
else:
302-
def link(self, src, dst):
303-
raise NotImplementedError("os.link() not available on this system")
304-
305-
rmdir = os.rmdir
306-
307-
rename = os.rename
308-
309-
replace = os.replace
310-
311-
if hasattr(os, "symlink"):
312-
symlink = os.symlink
313-
else:
314-
def symlink(self, src, dst, target_is_directory=False):
315-
raise NotImplementedError("os.symlink() not available on this system")
316-
317-
def touch(self, path, mode=0o666, exist_ok=True):
318-
if exist_ok:
319-
# First try to bump modification time
320-
# Implementation note: GNU touch uses the UTIME_NOW option of
321-
# the utimensat() / futimens() functions.
322-
try:
323-
os.utime(path, None)
324-
except OSError:
325-
# Avoid exception chaining
326-
pass
327-
else:
328-
return
329-
flags = os.O_CREAT | os.O_WRONLY
330-
if not exist_ok:
331-
flags |= os.O_EXCL
332-
fd = os.open(path, flags, mode)
333-
os.close(fd)
334-
335-
if hasattr(os, "readlink"):
336-
readlink = os.readlink
337-
else:
338-
def readlink(self, path):
339-
raise NotImplementedError("os.readlink() not available on this system")
340-
341-
def owner(self, path):
342-
try:
343-
import pwd
344-
return pwd.getpwuid(self.stat(path).st_uid).pw_name
345-
except ImportError:
346-
raise NotImplementedError("Path.owner() is unsupported on this system")
347-
348-
def group(self, path):
349-
try:
350-
import grp
351-
return grp.getgrgid(self.stat(path).st_gid).gr_name
352-
except ImportError:
353-
raise NotImplementedError("Path.group() is unsupported on this system")
354-
355-
getcwd = os.getcwd
356-
357-
expanduser = staticmethod(os.path.expanduser)
358-
359-
realpath = staticmethod(os.path.realpath)
360-
361-
362-
_normal_accessor = _NormalAccessor()
363-
364-
365278
#
366279
# Globbing helpers
367280
#
@@ -402,7 +315,7 @@ def select_from(self, parent_path):
402315
path_cls = type(parent_path)
403316
is_dir = path_cls.is_dir
404317
exists = path_cls.exists
405-
scandir = parent_path._accessor.scandir
318+
scandir = path_cls._scandir
406319
if not is_dir(parent_path):
407320
return iter([])
408321
return self._select_from(parent_path, is_dir, exists, scandir)
@@ -949,7 +862,6 @@ class Path(PurePath):
949862
object. You can also instantiate a PosixPath or WindowsPath directly,
950863
but cannot instantiate a WindowsPath on a POSIX system or vice versa.
951864
"""
952-
_accessor = _normal_accessor
953865
__slots__ = ()
954866

955867
def __new__(cls, *args, **kwargs):
@@ -988,7 +900,7 @@ def cwd(cls):
988900
"""Return a new path pointing to the current working directory
989901
(as returned by os.getcwd()).
990902
"""
991-
return cls(cls._accessor.getcwd())
903+
return cls(os.getcwd())
992904

993905
@classmethod
994906
def home(cls):
@@ -1005,16 +917,22 @@ def samefile(self, other_path):
1005917
try:
1006918
other_st = other_path.stat()
1007919
except AttributeError:
1008-
other_st = self._accessor.stat(other_path)
920+
other_st = self.__class__(other_path).stat()
1009921
return os.path.samestat(st, other_st)
1010922

1011923
def iterdir(self):
1012924
"""Iterate over the files in this directory. Does not yield any
1013925
result for the special paths '.' and '..'.
1014926
"""
1015-
for name in self._accessor.listdir(self):
927+
for name in os.listdir(self):
1016928
yield self._make_child_relpath(name)
1017929

930+
def _scandir(self):
931+
# bpo-24132: a future version of pathlib will support subclassing of
932+
# pathlib.Path to customize how the filesystem is accessed. This
933+
# includes scandir(), which is used to implement glob().
934+
return os.scandir(self)
935+
1018936
def glob(self, pattern):
1019937
"""Iterate over this subtree and yield all existing files (of any
1020938
kind, including directories) matching the given relative pattern.
@@ -1050,7 +968,7 @@ def absolute(self):
1050968
"""
1051969
if self.is_absolute():
1052970
return self
1053-
return self._from_parts([self._accessor.getcwd()] + self._parts)
971+
return self._from_parts([self.cwd()] + self._parts)
1054972

1055973
def resolve(self, strict=False):
1056974
"""
@@ -1064,7 +982,7 @@ def check_eloop(e):
1064982
raise RuntimeError("Symlink loop from %r" % e.filename)
1065983

1066984
try:
1067-
s = self._accessor.realpath(self, strict=strict)
985+
s = os.path.realpath(self, strict=strict)
1068986
except OSError as e:
1069987
check_eloop(e)
1070988
raise
@@ -1084,19 +1002,28 @@ def stat(self, *, follow_symlinks=True):
10841002
Return the result of the stat() system call on this path, like
10851003
os.stat() does.
10861004
"""
1087-
return self._accessor.stat(self, follow_symlinks=follow_symlinks)
1005+
return os.stat(self, follow_symlinks=follow_symlinks)
10881006

10891007
def owner(self):
10901008
"""
10911009
Return the login name of the file owner.
10921010
"""
1093-
return self._accessor.owner(self)
1011+
try:
1012+
import pwd
1013+
return pwd.getpwuid(self.stat().st_uid).pw_name
1014+
except ImportError:
1015+
raise NotImplementedError("Path.owner() is unsupported on this system")
10941016

10951017
def group(self):
10961018
"""
10971019
Return the group name of the file gid.
10981020
"""
1099-
return self._accessor.group(self)
1021+
1022+
try:
1023+
import grp
1024+
return grp.getgrgid(self.stat().st_gid).gr_name
1025+
except ImportError:
1026+
raise NotImplementedError("Path.group() is unsupported on this system")
11001027

11011028
def open(self, mode='r', buffering=-1, encoding=None,
11021029
errors=None, newline=None):
@@ -1106,8 +1033,7 @@ def open(self, mode='r', buffering=-1, encoding=None,
11061033
"""
11071034
if "b" not in mode:
11081035
encoding = io.text_encoding(encoding)
1109-
return self._accessor.open(self, mode, buffering, encoding, errors,
1110-
newline)
1036+
return io.open(self, mode, buffering, encoding, errors, newline)
11111037

11121038
def read_bytes(self):
11131039
"""
@@ -1148,21 +1074,38 @@ def readlink(self):
11481074
"""
11491075
Return the path to which the symbolic link points.
11501076
"""
1151-
path = self._accessor.readlink(self)
1152-
return self._from_parts((path,))
1077+
if not hasattr(os, "readlink"):
1078+
raise NotImplementedError("os.readlink() not available on this system")
1079+
return self._from_parts((os.readlink(self),))
11531080

11541081
def touch(self, mode=0o666, exist_ok=True):
11551082
"""
11561083
Create this file with the given access mode, if it doesn't exist.
11571084
"""
1158-
self._accessor.touch(self, mode, exist_ok)
1085+
1086+
if exist_ok:
1087+
# First try to bump modification time
1088+
# Implementation note: GNU touch uses the UTIME_NOW option of
1089+
# the utimensat() / futimens() functions.
1090+
try:
1091+
os.utime(self, None)
1092+
except OSError:
1093+
# Avoid exception chaining
1094+
pass
1095+
else:
1096+
return
1097+
flags = os.O_CREAT | os.O_WRONLY
1098+
if not exist_ok:
1099+
flags |= os.O_EXCL
1100+
fd = os.open(self, flags, mode)
1101+
os.close(fd)
11591102

11601103
def mkdir(self, mode=0o777, parents=False, exist_ok=False):
11611104
"""
11621105
Create a new directory at this given path.
11631106
"""
11641107
try:
1165-
self._accessor.mkdir(self, mode)
1108+
os.mkdir(self, mode)
11661109
except FileNotFoundError:
11671110
if not parents or self.parent == self:
11681111
raise
@@ -1178,7 +1121,7 @@ def chmod(self, mode, *, follow_symlinks=True):
11781121
"""
11791122
Change the permissions of the path, like os.chmod().
11801123
"""
1181-
self._accessor.chmod(self, mode, follow_symlinks=follow_symlinks)
1124+
os.chmod(self, mode, follow_symlinks=follow_symlinks)
11821125

11831126
def lchmod(self, mode):
11841127
"""
@@ -1193,7 +1136,7 @@ def unlink(self, missing_ok=False):
11931136
If the path is a directory, use rmdir() instead.
11941137
"""
11951138
try:
1196-
self._accessor.unlink(self)
1139+
os.unlink(self)
11971140
except FileNotFoundError:
11981141
if not missing_ok:
11991142
raise
@@ -1202,7 +1145,7 @@ def rmdir(self):
12021145
"""
12031146
Remove this directory. The directory must be empty.
12041147
"""
1205-
self._accessor.rmdir(self)
1148+
os.rmdir(self)
12061149

12071150
def lstat(self):
12081151
"""
@@ -1221,7 +1164,7 @@ def rename(self, target):
12211164
12221165
Returns the new Path instance pointing to the target path.
12231166
"""
1224-
self._accessor.rename(self, target)
1167+
os.rename(self, target)
12251168
return self.__class__(target)
12261169

12271170
def replace(self, target):
@@ -1234,23 +1177,27 @@ def replace(self, target):
12341177
12351178
Returns the new Path instance pointing to the target path.
12361179
"""
1237-
self._accessor.replace(self, target)
1180+
os.replace(self, target)
12381181
return self.__class__(target)
12391182

12401183
def symlink_to(self, target, target_is_directory=False):
12411184
"""
12421185
Make this path a symlink pointing to the target path.
12431186
Note the order of arguments (link, target) is the reverse of os.symlink.
12441187
"""
1245-
self._accessor.symlink(target, self, target_is_directory)
1188+
if not hasattr(os, "symlink"):
1189+
raise NotImplementedError("os.symlink() not available on this system")
1190+
os.symlink(target, self, target_is_directory)
12461191

12471192
def hardlink_to(self, target):
12481193
"""
12491194
Make this path a hard link pointing to the same file as *target*.
12501195
12511196
Note the order of arguments (self, target) is the reverse of os.link's.
12521197
"""
1253-
self._accessor.link(target, self)
1198+
if not hasattr(os, "link"):
1199+
raise NotImplementedError("os.link() not available on this system")
1200+
os.link(target, self)
12541201

12551202
def link_to(self, target):
12561203
"""
@@ -1268,7 +1215,7 @@ def link_to(self, target):
12681215
"for removal in Python 3.12. "
12691216
"Use pathlib.Path.hardlink_to() instead.",
12701217
DeprecationWarning, stacklevel=2)
1271-
self._accessor.link(self, target)
1218+
self.__class__(target).hardlink_to(self)
12721219

12731220
# Convenience functions for querying the stat results
12741221

@@ -1425,7 +1372,7 @@ def expanduser(self):
14251372
"""
14261373
if (not (self._drv or self._root) and
14271374
self._parts and self._parts[0][:1] == '~'):
1428-
homedir = self._accessor.expanduser(self._parts[0])
1375+
homedir = os.path.expanduser(self._parts[0])
14291376
if homedir[:1] == "~":
14301377
raise RuntimeError("Could not determine home directory.")
14311378
return self._from_parts([homedir] + self._parts[1:])

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy