Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 9cf065c

Browse files
committed
Issue #14626: Large refactoring of functions / parameters in the os module.
Many functions now support "dir_fd" and "follow_symlinks" parameters; some also support accepting an open file descriptor in place of of a path string. Added os.support_* collections as LBYL helpers. Removed many functions only previously seen in 3.3 alpha releases (often starting with "f" or "l", or ending with "at"). Originally suggested by Serhiy Storchaka; implemented by Larry Hastings.
1 parent f0f4742 commit 9cf065c

9 files changed

Lines changed: 3378 additions & 3006 deletions

File tree

Doc/library/os.rst

Lines changed: 480 additions & 418 deletions
Large diffs are not rendered by default.

Lib/os.py

Lines changed: 104 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,10 @@ def _get_exports_list(module):
5656
pass
5757
import posixpath as path
5858

59-
import posix
60-
__all__.extend(_get_exports_list(posix))
61-
del posix
59+
try:
60+
from posix import _have_functions
61+
except ImportError:
62+
pass
6263

6364
elif 'nt' in _names:
6465
name = 'nt'
@@ -75,6 +76,11 @@ def _get_exports_list(module):
7576
__all__.extend(_get_exports_list(nt))
7677
del nt
7778

79+
try:
80+
from nt import _have_functions
81+
except ImportError:
82+
pass
83+
7884
elif 'os2' in _names:
7985
name = 'os2'
8086
linesep = '\r\n'
@@ -94,6 +100,11 @@ def _get_exports_list(module):
94100
__all__.extend(_get_exports_list(os2))
95101
del os2
96102

103+
try:
104+
from os2 import _have_functions
105+
except ImportError:
106+
pass
107+
97108
elif 'ce' in _names:
98109
name = 'ce'
99110
linesep = '\r\n'
@@ -110,6 +121,11 @@ def _get_exports_list(module):
110121
__all__.extend(_get_exports_list(ce))
111122
del ce
112123

124+
try:
125+
from ce import _have_functions
126+
except ImportError:
127+
pass
128+
113129
else:
114130
raise ImportError('no os specific module found')
115131

@@ -119,6 +135,84 @@ def _get_exports_list(module):
119135

120136
del _names
121137

138+
139+
if _exists("_have_functions"):
140+
_globals = globals()
141+
def _add(str, fn):
142+
if (fn in _globals) and (str in _have_functions):
143+
_set.add(_globals[fn])
144+
145+
_set = set()
146+
_add("HAVE_FACCESSAT", "access")
147+
_add("HAVE_FCHMODAT", "chmod")
148+
_add("HAVE_FCHOWNAT", "chown")
149+
_add("HAVE_FSTATAT", "stat")
150+
_add("HAVE_FUTIMESAT", "utime")
151+
_add("HAVE_LINKAT", "link")
152+
_add("HAVE_MKDIRAT", "mkdir")
153+
_add("HAVE_MKFIFOAT", "mkfifo")
154+
_add("HAVE_MKNODAT", "mknod")
155+
_add("HAVE_OPENAT", "open")
156+
_add("HAVE_READLINKAT", "readlink")
157+
_add("HAVE_RENAMEAT", "rename")
158+
_add("HAVE_SYMLINKAT", "symlink")
159+
_add("HAVE_UNLINKAT", "unlink")
160+
_add("HAVE_UTIMENSAT", "utime")
161+
supports_dir_fd = _set
162+
163+
_set = set()
164+
_add("HAVE_FACCESSAT", "access")
165+
supports_effective_ids = _set
166+
167+
_set = set()
168+
_add("HAVE_FCHDIR", "chdir")
169+
_add("HAVE_FCHMOD", "chmod")
170+
_add("HAVE_FCHOWN", "chown")
171+
_add("HAVE_FDOPENDIR", "listdir")
172+
_add("HAVE_FEXECVE", "execve")
173+
_set.add(stat) # fstat always works
174+
_add("HAVE_FUTIMENS", "utime")
175+
_add("HAVE_FUTIMES", "utime")
176+
if _exists("statvfs") and _exists("fstatvfs"): # mac os x10.3
177+
_add("HAVE_FSTATVFS", "statvfs")
178+
supports_fd = _set
179+
180+
_set = set()
181+
_add("HAVE_FACCESSAT", "access")
182+
# Current linux (kernel 3.2, glibc 2.15) doesn't support lchmod.
183+
# (The function exists, but it's a stub that always returns ENOSUP.)
184+
# Now, linux *does* have fchmodat, which says it can ignore
185+
# symbolic links. But that doesn't work either (also returns ENOSUP).
186+
# I'm guessing that if they fix fchmodat, they'll also add lchmod at
187+
# the same time. So, for now, assume that fchmodat doesn't support
188+
# follow_symlinks unless lchmod works.
189+
if ((sys.platform != "linux") or
190+
("HAVE_LCHMOD" in _have_functions)):
191+
_add("HAVE_FCHMODAT", "chmod")
192+
_add("HAVE_FCHOWNAT", "chown")
193+
_add("HAVE_FSTATAT", "stat")
194+
_add("HAVE_LCHFLAGS", "chflags")
195+
_add("HAVE_LCHMOD", "chmod")
196+
if _exists("lchown"): # mac os x10.3
197+
_add("HAVE_LCHOWN", "chown")
198+
_add("HAVE_LINKAT", "link")
199+
_add("HAVE_LUTIMES", "utime")
200+
_add("HAVE_LSTAT", "stat")
201+
_add("HAVE_FSTATAT", "stat")
202+
_add("HAVE_UTIMENSAT", "utime")
203+
_add("MS_WINDOWS", "stat")
204+
supports_follow_symlinks = _set
205+
206+
_set = set()
207+
_add("HAVE_UNLINKAT", "unlink")
208+
supports_remove_directory = _set
209+
210+
del _set
211+
del _have_functions
212+
del _globals
213+
del _add
214+
215+
122216
# Python uses fixed values for the SEEK_ constants; they are mapped
123217
# to native constants if necessary in posixmodule.c
124218
# Other possible SEEK values are directly imported from posixmodule.c
@@ -318,7 +412,7 @@ def walk(top, topdown=True, onerror=None, followlinks=False):
318412

319413
__all__.append("walk")
320414

321-
if _exists("openat"):
415+
if open in supports_dir_fd:
322416

323417
def fwalk(top, topdown=True, onerror=None, followlinks=False):
324418
"""Directory tree generator.
@@ -343,7 +437,7 @@ def fwalk(top, topdown=True, onerror=None, followlinks=False):
343437
import os
344438
for root, dirs, files, rootfd in os.fwalk('python/Lib/email'):
345439
print(root, "consumes", end="")
346-
print(sum([os.fstatat(rootfd, name).st_size for name in files]),
440+
print(sum([os.stat(name, dir_fd=rootfd).st_size for name in files]),
347441
end="")
348442
print("bytes in", len(files), "non-directory files")
349443
if 'CVS' in dirs:
@@ -365,25 +459,22 @@ def _fwalk(topfd, toppath, topdown, onerror, followlinks):
365459
# necessary, it can be adapted to only require O(1) FDs, see issue
366460
# #13734.
367461

368-
# whether to follow symlinks
369-
flag = 0 if followlinks else AT_SYMLINK_NOFOLLOW
370-
371-
names = flistdir(topfd)
462+
names = listdir(topfd)
372463
dirs, nondirs = [], []
373464
for name in names:
374465
try:
375466
# Here, we don't use AT_SYMLINK_NOFOLLOW to be consistent with
376467
# walk() which reports symlinks to directories as directories.
377468
# We do however check for symlinks before recursing into
378469
# a subdirectory.
379-
if st.S_ISDIR(fstatat(topfd, name).st_mode):
470+
if st.S_ISDIR(stat(name, dir_fd=topfd).st_mode):
380471
dirs.append(name)
381472
else:
382473
nondirs.append(name)
383474
except FileNotFoundError:
384475
try:
385476
# Add dangling symlinks, ignore disappeared files
386-
if st.S_ISLNK(fstatat(topfd, name, AT_SYMLINK_NOFOLLOW)
477+
if st.S_ISLNK(stat(name, dir_fd=topfd, follow_symlinks=False)
387478
.st_mode):
388479
nondirs.append(name)
389480
except FileNotFoundError:
@@ -394,8 +485,8 @@ def _fwalk(topfd, toppath, topdown, onerror, followlinks):
394485

395486
for name in dirs:
396487
try:
397-
orig_st = fstatat(topfd, name, flag)
398-
dirfd = openat(topfd, name, O_RDONLY)
488+
orig_st = stat(name, dir_fd=topfd, follow_symlinks=followlinks)
489+
dirfd = open(name, O_RDONLY, dir_fd=topfd)
399490
except error as err:
400491
if onerror is not None:
401492
onerror(err)

Lib/shutil.py

Lines changed: 36 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -139,27 +139,45 @@ def copystat(src, dst, symlinks=False):
139139
only if both `src` and `dst` are symlinks.
140140
141141
"""
142-
def _nop(*args, ns=None):
142+
def _nop(*args, ns=None, follow_symlinks=None):
143143
pass
144144

145-
if symlinks and os.path.islink(src) and os.path.islink(dst):
146-
stat_func = os.lstat
147-
utime_func = os.lutimes if hasattr(os, 'lutimes') else _nop
148-
chmod_func = os.lchmod if hasattr(os, 'lchmod') else _nop
149-
chflags_func = os.lchflags if hasattr(os, 'lchflags') else _nop
145+
# follow symlinks (aka don't not follow symlinks)
146+
follow = not (symlinks and os.path.islink(src) and os.path.islink(dst))
147+
if follow:
148+
# use the real function if it exists
149+
def lookup(name):
150+
return getattr(os, name, _nop)
150151
else:
151-
stat_func = os.stat
152-
utime_func = os.utime if hasattr(os, 'utime') else _nop
153-
chmod_func = os.chmod if hasattr(os, 'chmod') else _nop
154-
chflags_func = os.chflags if hasattr(os, 'chflags') else _nop
155-
156-
st = stat_func(src)
152+
# use the real function only if it exists
153+
# *and* it supports follow_symlinks
154+
def lookup(name):
155+
fn = getattr(os, name, _nop)
156+
if fn in os.supports_follow_symlinks:
157+
return fn
158+
return _nop
159+
160+
st = lookup("stat")(src, follow_symlinks=follow)
157161
mode = stat.S_IMODE(st.st_mode)
158-
utime_func(dst, ns=(st.st_atime_ns, st.st_mtime_ns))
159-
chmod_func(dst, mode)
162+
lookup("utime")(dst, ns=(st.st_atime_ns, st.st_mtime_ns),
163+
follow_symlinks=follow)
164+
try:
165+
lookup("chmod")(dst, mode, follow_symlinks=follow)
166+
except NotImplementedError:
167+
# if we got a NotImplementedError, it's because
168+
# * follow_symlinks=False,
169+
# * lchown() is unavailable, and
170+
# * either
171+
# * fchownat() is unvailable or
172+
# * fchownat() doesn't implement AT_SYMLINK_NOFOLLOW.
173+
# (it returned ENOSUP.)
174+
# therefore we're out of options--we simply cannot chown the
175+
# symlink. give up, suppress the error.
176+
# (which is what shutil always did in this circumstance.)
177+
pass
160178
if hasattr(st, 'st_flags'):
161179
try:
162-
chflags_func(dst, st.st_flags)
180+
lookup("chflags")(dst, st.st_flags, follow_symlinks=follow)
163181
except OSError as why:
164182
for err in 'EOPNOTSUPP', 'ENOTSUP':
165183
if hasattr(errno, err) and why.errno == getattr(errno, err):
@@ -176,20 +194,11 @@ def _copyxattr(src, dst, symlinks=False):
176194
If the optional flag `symlinks` is set, symlinks won't be followed.
177195
178196
"""
179-
if symlinks:
180-
listxattr = os.llistxattr
181-
removexattr = os.lremovexattr
182-
setxattr = os.lsetxattr
183-
getxattr = os.lgetxattr
184-
else:
185-
listxattr = os.listxattr
186-
removexattr = os.removexattr
187-
setxattr = os.setxattr
188-
getxattr = os.getxattr
189197

190-
for attr in listxattr(src):
198+
for name in os.listxattr(src, follow_symlinks=symlinks):
191199
try:
192-
setxattr(dst, attr, getxattr(src, attr))
200+
value = os.getxattr(src, name, follow_symlinks=symlinks)
201+
os.setxattr(dst, name, value, follow_symlinks=symlinks)
193202
except OSError as e:
194203
if e.errno not in (errno.EPERM, errno.ENOTSUP, errno.ENODATA):
195204
raise

Lib/test/support.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1703,8 +1703,8 @@ def can_xattr():
17031703
try:
17041704
# TESTFN & tempfile may use different file systems with
17051705
# different capabilities
1706-
os.fsetxattr(tmp_fp, b"user.test", b"")
1707-
os.fsetxattr(fp.fileno(), b"user.test", b"")
1706+
os.setxattr(tmp_fp, b"user.test", b"")
1707+
os.setxattr(fp.fileno(), b"user.test", b"")
17081708
# Kernels < 2.6.39 don't respect setxattr flags.
17091709
kernel_version = platform.release()
17101710
m = re.match("2.6.(\d{1,2})", kernel_version)

0 commit comments

Comments
 (0)